def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: ToolException, if creation failed. """ client = self.context['container_client'] messages = self.context['container_messages'] cluster_ref = util.ParseCluster(args.name, self.context) if args.password: password = args.password else: password = ''.join( random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)) node_config = messages.NodeConfig() if args.machine_type: node_config.machineType = args.machine_type if args.source_image: node_config.sourceImage = args.source_image node_config.serviceAccounts = self.CreateServiceAccountMessages( args, messages) create_cluster_req = messages.CreateClusterRequest( cluster=messages.Cluster( name=cluster_ref.clusterId, numNodes=args.num_nodes, nodeConfig=node_config, masterAuth=messages.MasterAuth(user=args.user, password=password), enableCloudLogging=args.enable_cloud_logging)) if args.cluster_api_version: create_cluster_req.cluster.clusterApiVersion = args.cluster_api_version if args.network: create_cluster_req.cluster.network = args.network if args.container_ipv4_cidr: create_cluster_req.cluster.containerIpv4Cidr = args.container_ipv4_cidr req = messages.ContainerProjectsZonesClustersCreateRequest( createClusterRequest=create_cluster_req, projectId=cluster_ref.projectId, zoneId=cluster_ref.zoneId) cluster = None try: operation = client.projects_zones_clusters.Create(req) if not args.wait: return util.DescribeCluster(cluster_ref, self.context) operation = util.WaitForOperation( operation, cluster_ref.projectId, self.context, 'Creating cluster {0}'.format(cluster_ref.clusterId)) # Get Cluster cluster = util.DescribeCluster(cluster_ref, self.context) except apitools_base.HttpError as error: raise exceptions.HttpException(util.GetError(error)) log.CreatedResource(cluster_ref) # Persist cluster config c_config = util.ClusterConfig.Persist(cluster, cluster_ref.projectId, self.cli) if c_config: if not c_config.has_certs: # Purge config so we retry the cert fetch on next kubectl command util.ClusterConfig.Purge(cluster.name, cluster.zone, cluster_ref.projectId) # Exit with non-success returncode if certs could not be fetched self.exit_code = 1 else: # Set current-context to new cluster if one is not already set kubeconfig = kconfig.Kubeconfig.Default() if not kubeconfig.current_context: kubeconfig.SetCurrentContext(c_config.kube_context) kubeconfig.SaveToFile() return cluster
class Create(base.CreateCommand): """Create a cluster for running containers.""" @staticmethod def Args(parser): _Args(parser) _AddAdditionalZonesFlag(parser, deprecated=True) flags.AddNodeLocationsFlag(parser) flags.AddAddonsFlags(parser) flags.AddClusterAutoscalingFlags(parser) flags.AddDiskTypeFlag(parser, suppressed=True) flags.AddEnableAutoRepairFlag(parser, for_create=True) flags.AddEnableKubernetesAlphaFlag(parser) flags.AddEnableLegacyAuthorizationFlag(parser) flags.AddIPAliasFlags(parser) flags.AddLabelsFlag(parser) flags.AddLocalSSDFlag(parser) flags.AddMaintenanceWindowFlag(parser) flags.AddMasterAuthorizedNetworksFlags(parser) flags.AddMinCpuPlatformFlag(parser) flags.AddNetworkPolicyFlags(parser) flags.AddNodeTaintsFlag(parser) flags.AddPreemptibleFlag(parser) flags.AddDeprecatedClusterNodeIdentityFlags(parser) def ParseCreateOptions(self, args): return ParseCreateOptionsBase(args) def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: util.Error, if creation failed. """ if args. async and not args.IsSpecified('format'): args.format = util.OPERATIONS_FORMAT util.CheckKubectlInstalled() adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) options = self.ParseCreateOptions(args) if options.enable_kubernetes_alpha: console_io.PromptContinue( message=constants.KUBERNETES_ALPHA_PROMPT, throw_if_unattended=True, cancel_on_no=True) if getattr(args, 'region', None): # TODO(b/68496825): Remove this completely after regional clusters beta # launch. if self._release_track == base.ReleaseTrack.ALPHA: console_io.PromptContinue( message=constants.KUBERNETES_REGIONAL_CHARGES_PROMPT, throw_if_unattended=True, cancel_on_no=True) if options.enable_autorepair is not None: log.status.Print( messages.AutoUpdateUpgradeRepairMessage( options.enable_autorepair, 'autorepair')) if options.enable_autoupgrade is not None: log.status.Print( messages.AutoUpdateUpgradeRepairMessage( options.enable_autoupgrade, 'autoupgrade')) if options.accelerators is not None: log.status.Print(constants.KUBERNETES_GPU_LIMITATION_MSG) operation = None try: operation_ref = adapter.CreateCluster(cluster_ref, options) if args. async: return adapter.GetCluster(cluster_ref) operation = adapter.WaitForOperation(operation_ref, 'Creating cluster {0}'.format( cluster_ref.clusterId), timeout_s=args.timeout) cluster = adapter.GetCluster(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) log.CreatedResource(cluster_ref) cluster_url = util.GenerateClusterUrl(cluster_ref) log.status.Print('To inspect the contents of your cluster, go to: ' + cluster_url) if operation.detail: # Non-empty detail on a DONE create operation should be surfaced as # a warning to end user. log.warning(operation.detail) try: util.ClusterConfig.Persist(cluster, cluster_ref.projectId) except kconfig.MissingEnvVarError as error: log.warning(error) return [cluster]
def Run(self, args): """Run 'deployments delete'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns boolean indicating whether insert operation succeeded. Raises: HttpException: An http error response was received while executing api request. ToolException: The deployment deletion operation encountered an error. """ client = self.context['deploymentmanager-client'] messages = self.context['deploymentmanager-messages'] project = properties.VALUES.core.project.Get(required=True) prompt_message = ('The following deployments will be deleted:\n- ' + '\n- '.join(args.deployment_name)) if not args.quiet: if not console_io.PromptContinue(message=prompt_message, default=False): raise exceptions.ToolException('Deletion aborted by user.') operations = [] for deployment_name in args.deployment_name: try: operation = client.deployments.Delete( messages.DeploymentmanagerDeploymentsDeleteRequest( project=project, deployment=deployment_name, )) except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) if args. async: operations.append(operation) else: op_name = operation.name try: dm_v2_util.WaitForOperation(op_name, project, self.context, 'delete', OPERATION_TIMEOUT) log.status.Print('Delete operation ' + op_name + ' completed successfully.') except (exceptions.ToolException, DeploymentManagerError): log.error('Delete operation ' + op_name + ' has errors or failed to complete within in ' + str(OPERATION_TIMEOUT) + ' seconds.') except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) try: completed_operation = client.operations.Get( messages.DeploymentmanagerOperationsGetRequest( project=project, operation=op_name, )) except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) operations.append(completed_operation) return operations
def Run(self, args): """Run 'deployments stop'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns boolean indicating whether stop operation succeeded. Raises: HttpException: An http error response was received while executing api request. """ if args.fingerprint: fingerprint = dm_util.DecodeFingerprint(args.fingerprint) else: # If no fingerprint is present, default to an empty fingerprint. # TODO(b/34966984): Remove the empty default after cleaning up all # deployments that has no fingerprint fingerprint = dm_v2_util.FetchDeploymentFingerprint( dm_base.GetClient(), dm_base.GetMessages(), dm_base.GetProject(), args.deployment_name) or '' try: operation = dm_base.GetClient().deployments.Stop( dm_base.GetMessages().DeploymentmanagerDeploymentsStopRequest( project=dm_base.GetProject(), deployment=args.deployment_name, deploymentsStopRequest=( dm_base.GetMessages().DeploymentsStopRequest( fingerprint=fingerprint) ), ) ) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_v2_util.HTTP_ERROR_FORMAT) if args.async: return operation else: op_name = operation.name try: dm_write.WaitForOperation(op_name, 'stop', dm_base.GetProject(), timeout=OPERATION_TIMEOUT) log.status.Print('Stop operation ' + op_name + ' completed successfully.') except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_v2_util.HTTP_ERROR_FORMAT) try: # Fetch a list of the stopped resources. response = dm_base.GetClient().resources.List( dm_base.GetMessages().DeploymentmanagerResourcesListRequest( project=dm_base.GetProject(), deployment=args.deployment_name, ) ) # TODO(b/36055861): Pagination return response.resources if response.resources else [] except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_v2_util.HTTP_ERROR_FORMAT)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: util.Error, if creation failed. """ util.CheckKubectlInstalled() if not args.password: args.password = ''.join( random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)) adapter = self.context['api_adapter'] if not args.scopes: args.scopes = [] cluster_ref = adapter.ParseCluster(args.name) options = self.ParseCreateOptions(args) operation = None try: operation_ref = adapter.CreateCluster(cluster_ref, options) if not args.wait: return adapter.GetCluster(cluster_ref) operation = adapter.WaitForOperation(operation_ref, 'Creating cluster {0}'.format( cluster_ref.clusterId), timeout_s=args.timeout) cluster = adapter.GetCluster(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(util.GetError(error)) log.CreatedResource(cluster_ref) if operation.detail: # Non-empty detail on a DONE create operation should be surfaced as # a warning to end user. log.warning(operation.detail) # Persist cluster config current_context = kconfig.Kubeconfig.Default().current_context c_config = util.ClusterConfig.Persist(cluster, cluster_ref.projectId) if not c_config.has_certs: # Purge config so we retry the cert fetch on next kubectl command util.ClusterConfig.Purge(cluster.name, cluster.zone, cluster_ref.projectId) # reset current context if current_context: kubeconfig = kconfig.Kubeconfig.Default() kubeconfig.SetCurrentContext(current_context) kubeconfig.SaveToFile() raise util.Error( NO_CERTS_ERROR_FMT.format( command=' '.join(args.command_path[:-1] + ['get-credentials', cluster.name]))) return cluster
def Func(*args, **kwargs): if args and args[0].startswith('projects/'): raise sdk_ex.HttpException('Topics and/or subscription names' ' should not start with the prefix' ' "projects/"') return f(*args, **kwargs)
def RunBaseCreateCommand(args, release_track): """Creates a new Cloud SQL instance. Args: args: argparse.Namespace, The arguments that this command was invoked with. release_track: base.ReleaseTrack, the release track that this was run under. Returns: A dict object representing the operations resource describing the create operation if the create was successful. Raises: HttpException: A http error response was received while executing api request. """ client = common_api_util.SqlClient(common_api_util.API_VERSION_DEFAULT) sql_client = client.sql_client sql_messages = client.sql_messages validate.ValidateInstanceName(args.instance) instance_ref = client.resource_parser.Parse( args.instance, params={'project': properties.VALUES.core.project.GetOrFail}, collection='sql.instances') # Get the region, tier, and database version from the master if these fields # are not specified. # TODO(b/64266672): Remove once API does not require these fields. if args.IsSpecified('master_instance_name'): master_instance_ref = client.resource_parser.Parse( args.master_instance_name, params={'project': properties.VALUES.core.project.GetOrFail}, collection='sql.instances') try: master_instance_resource = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest( project=instance_ref.project, instance=master_instance_ref.instance)) except apitools_exceptions.HttpError as error: # TODO(b/64292220): Remove once API gives helpful error message. log.debug('operation : %s', six.text_type(master_instance_ref)) exc = exceptions.HttpException(error) if resource_property.Get( exc.payload.content, resource_lex.ParseKey('error.errors[0].reason'), None) == 'notAuthorized': msg = ( 'You are either not authorized to access the master instance or ' 'it does not exist.') raise exceptions.HttpException(msg) raise if not args.IsSpecified('region'): args.region = master_instance_resource.region if not args.IsSpecified('database_version'): args.database_version = master_instance_resource.databaseVersion if not args.IsSpecified('tier') and master_instance_resource.settings: args.tier = master_instance_resource.settings.tier # Check for CMEK usage; warn the user about replica inheriting the setting. if master_instance_resource.diskEncryptionConfiguration: command_util.ShowCmekWarning('replica', 'the master instance') # --root-password is required when creating SQL Server instances if args.IsSpecified( 'database_version') and args.database_version.startswith( 'SQLSERVER') and not args.IsSpecified('root_password'): raise exceptions.RequiredArgumentException( '--root-password', '`--root-password` is required when creating SQL Server instances.' ) instance_resource = ( command_util.InstancesV1Beta4.ConstructCreateInstanceFromArgs( sql_messages, args, instance_ref=instance_ref, release_track=release_track)) # TODO(b/122660263): Remove when V1 instances are no longer supported. # V1 instances are deprecated. Prompt to continue if one is being created. if api_util.IsInstanceV1(instance_resource): log.warning( 'First Generation instances will be automatically upgraded ' 'to Second Generation starting March 4th, 2020, and First Generation ' 'will be fully decommissioned on March 25, 2020. We recommend you ' 'create a Second Generation instance.') console_io.PromptContinue(cancel_on_no=True) if args.pricing_plan == 'PACKAGE': console_io.PromptContinue( 'Charges will begin accruing immediately. Really create Cloud ' 'SQL instance?', cancel_on_no=True) operation_ref = None try: result_operation = sql_client.instances.Insert(instance_resource) operation_ref = client.resource_parser.Create( 'sql.operations', operation=result_operation.name, project=instance_ref.project) if args.async_: if not args.IsSpecified('format'): args.format = 'default' return sql_client.operations.Get( sql_messages.SqlOperationsGetRequest( project=operation_ref.project, operation=operation_ref.operation)) operations.OperationsV1Beta4.WaitForOperation( sql_client, operation_ref, 'Creating Cloud SQL instance', # TODO(b/138403566): Remove the override once we improve creation times. max_wait_seconds=680) log.CreatedResource(instance_ref) new_resource = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest( project=instance_ref.project, instance=instance_ref.instance)) return new_resource except apitools_exceptions.HttpError as error: log.debug('operation : %s', six.text_type(operation_ref)) exc = exceptions.HttpException(error) if resource_property.Get( exc.payload.content, resource_lex.ParseKey('error.errors[0].reason'), None) == 'errorMaxInstancePerLabel': msg = resource_property.Get(exc.payload.content, resource_lex.ParseKey('error.message'), None) raise exceptions.HttpException(msg) raise
def RunBaseCreateCommand(args, release_track): """Creates a new Cloud SQL instance. Args: args: argparse.Namespace, The arguments that this command was invoked with. release_track: base.ReleaseTrack, the release track that this was run under. Returns: A dict object representing the operations resource describing the create operation if the create was successful. Raises: HttpException: A http error response was received while executing api request. ArgumentError: An argument supplied by the user was incorrect, such as specifying an invalid CMEK configuration or attempting to create a V1 instance. RequiredArgumentException: A required argument was not supplied by the user, such as omitting --root-password on a SQL Server instance. """ client = common_api_util.SqlClient(common_api_util.API_VERSION_DEFAULT) sql_client = client.sql_client sql_messages = client.sql_messages validate.ValidateInstanceName(args.instance) validate.ValidateInstanceLocation(args) instance_ref = client.resource_parser.Parse( args.instance, params={'project': properties.VALUES.core.project.GetOrFail}, collection='sql.instances') # Get the region, tier, and database version from the master if these fields # are not specified. # TODO(b/64266672): Remove once API does not require these fields. if args.IsSpecified('master_instance_name'): master_instance_ref = client.resource_parser.Parse( args.master_instance_name, params={'project': properties.VALUES.core.project.GetOrFail}, collection='sql.instances') try: master_instance_resource = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest( project=instance_ref.project, instance=master_instance_ref.instance)) except apitools_exceptions.HttpError as error: # TODO(b/64292220): Remove once API gives helpful error message. log.debug('operation : %s', six.text_type(master_instance_ref)) exc = exceptions.HttpException(error) if resource_property.Get( exc.payload.content, resource_lex.ParseKey('error.errors[0].reason'), None) == 'notAuthorized': msg = ( 'You are either not authorized to access the master instance or ' 'it does not exist.') raise exceptions.HttpException(msg) raise if not args.IsSpecified('region'): args.region = master_instance_resource.region if not args.IsSpecified('database_version'): args.database_version = master_instance_resource.databaseVersion.name if not args.IsSpecified('tier') and not ( args.IsSpecified('cpu') or args.IsSpecified('memory') ) and master_instance_resource.settings: args.tier = master_instance_resource.settings.tier # Validate master/replica CMEK configurations. if master_instance_resource.diskEncryptionConfiguration: if args.region == master_instance_resource.region: # Warn user that same-region replicas inherit their master's CMEK # configuration. command_util.ShowCmekWarning('replica', 'the master instance') elif not args.IsSpecified('disk_encryption_key'): # Raise error that cross-region replicas require their own CMEK key if # the master is CMEK. raise exceptions.RequiredArgumentException( '--disk-encryption-key', '`--disk-encryption-key` is required when creating a cross-region ' 'replica of an instance with customer-managed encryption.') else: command_util.ShowCmekWarning('replica') elif args.IsSpecified('disk_encryption_key'): # Raise error that cross-region replicas cannot be CMEK encrypted if their # master is not. raise sql_exceptions.ArgumentError( '`--disk-encryption-key` cannot be specified when creating a replica ' 'of an instance without customer-managed encryption.') # --root-password is required when creating SQL Server instances if args.IsSpecified( 'database_version') and args.database_version.startswith( 'SQLSERVER') and not args.IsSpecified('root_password'): raise exceptions.RequiredArgumentException( '--root-password', '`--root-password` is required when creating SQL Server instances.' ) if not args.backup: if args.IsSpecified('enable_bin_log'): raise sql_exceptions.ArgumentError( '`--enable-bin-log` cannot be specified when --no-backup is ' 'specified') elif args.IsSpecified('enable_point_in_time_recovery'): raise sql_exceptions.ArgumentError( '`--enable-point-in-time-recovery` cannot be specified when ' '--no-backup is specified') instance_resource = ( command_util.InstancesV1Beta4.ConstructCreateInstanceFromArgs( sql_messages, args, instance_ref=instance_ref, release_track=release_track)) # TODO(b/122660263): Remove when V1 instances are no longer supported. # V1 instances are deprecated. # Note that the exception type is intentionally vague because the user may not # have directly supplied the offending argument. For example, creating a read # replica defaults its tier to that of its master. if api_util.IsInstanceV1(sql_messages, instance_resource): raise sql_exceptions.ArgumentError( 'First Generation instances can no longer be created.') operation_ref = None try: result_operation = sql_client.instances.Insert(instance_resource) operation_ref = client.resource_parser.Create( 'sql.operations', operation=result_operation.name, project=instance_ref.project) if args.async_: if not args.IsSpecified('format'): args.format = 'default' return sql_client.operations.Get( sql_messages.SqlOperationsGetRequest( project=operation_ref.project, operation=operation_ref.operation)) operations.OperationsV1Beta4.WaitForOperation( sql_client, operation_ref, 'Creating Cloud SQL instance', # TODO(b/138403566): Remove the override once we improve creation times. max_wait_seconds=680) log.CreatedResource(instance_ref) new_resource = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest( project=instance_ref.project, instance=instance_ref.instance)) return new_resource except apitools_exceptions.HttpError as error: log.debug('operation : %s', six.text_type(operation_ref)) exc = exceptions.HttpException(error) if resource_property.Get( exc.payload.content, resource_lex.ParseKey('error.errors[0].reason'), None) == 'errorMaxInstancePerLabel': msg = resource_property.Get(exc.payload.content, resource_lex.ParseKey('error.message'), None) raise exceptions.HttpException(msg) raise
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: util.Error, if creation failed. """ util.CheckKubectlInstalled() adapter = self.context['api_adapter'] if not args.scopes: args.scopes = [] cluster_ref = adapter.ParseCluster(args.name) options = self.ParseCreateOptions(args) if options.enable_kubernetes_alpha: console_io.PromptContinue(message=constants.KUBERNETES_ALPHA_PROMPT, throw_if_unattended=True, cancel_on_no=True) if options.enable_autorepair is not None: log.status.Print(messages.AutoUpdateUpgradeRepairMessage( options.enable_autorepair, 'autorepair')) if options.enable_autoupgrade is not None: log.status.Print(messages.AutoUpdateUpgradeRepairMessage( options.enable_autoupgrade, 'autoupgrade')) operation = None try: operation_ref = adapter.CreateCluster(cluster_ref, options) if args.async: return adapter.GetCluster(cluster_ref) operation = adapter.WaitForOperation( operation_ref, 'Creating cluster {0}'.format(cluster_ref.clusterId), timeout_s=args.timeout) cluster = adapter.GetCluster(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) log.CreatedResource(cluster_ref) if operation.detail: # Non-empty detail on a DONE create operation should be surfaced as # a warning to end user. log.warning(operation.detail) try: util.ClusterConfig.Persist(cluster, cluster_ref.projectId) except kconfig.MissingEnvVarError as error: log.warning(error.message) return cluster
def CatchHTTPErrorRaiseHTTPException(*args, **kwargs): try: for result in func(*args, **kwargs): yield result except apitools_exceptions.HttpError as error: raise exceptions.HttpException(GetError(error))
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] cluster_ref = adapter.ParseCluster(args.name) # Make sure it exists (will raise appropriate error if not) adapter.GetCluster(cluster_ref) # locations will be None if additional-zones was specified, an empty list # if it was specified with no argument, or a populated list if zones were # provided. We want to distinguish between the case where it isn't # specified (and thus shouldn't be passed on to the API) and the case where # it's specified as wanting no additional zones, in which case we must pass # the cluster's primary zone to the API. # TODO(b/29578401): Remove the hasattr once the flag is GA. locations = None if hasattr(args, 'additional_zones') and args.additional_zones is not None: locations = sorted([cluster_ref.zone] + args.additional_zones) if args.generate_password or args.set_password: if args.generate_password: password = '' options = api_adapter.SetMasterAuthOptions( action=api_adapter.SetMasterAuthOptions.GENERATE_PASSWORD, password=password) else: password = raw_input('Please enter the new password:'******'Updating {0}'.format(cluster_ref.clusterId)) log.UpdatedResource(cluster_ref)
def CatchHTTPErrorRaiseHTTPException(*args, **kwargs): try: return func(*args, **kwargs) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(GetError(error))
def Run(self, args): """Creates a new Cloud SQL instance. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: A dict object representing the operations resource describing the create operation if the create was successful. Raises: HttpException: A http error response was received while executing api request. ToolException: An error other than http error occured while executing the command. """ sql_client = self.context['sql_client'] sql_messages = self.context['sql_messages'] resources = self.context['registry'] validate.ValidateInstanceName(args.instance) instance_ref = resources.Parse(args.instance, collection='sql.instances') instance_resource = instances.InstancesV1Beta4.ConstructInstanceFromArgs( sql_messages, args, instance_ref=instance_ref) if args.pricing_plan == 'PACKAGE': if not console_io.PromptContinue( 'Charges will begin accruing immediately. Really create Cloud ' 'SQL instance?'): raise exceptions.ToolException('canceled by the user.') operation_ref = None try: result_operation = sql_client.instances.Insert(instance_resource) operation_ref = resources.Create( 'sql.operations', operation=result_operation.name, project=instance_ref.project) if args.async: return sql_client.operations.Get( sql_messages.SqlOperationsGetRequest( project=operation_ref.project, operation=operation_ref.operation)) operations.OperationsV1Beta4.WaitForOperation( sql_client, operation_ref, 'Creating Cloud SQL instance') log.CreatedResource(instance_ref) new_resource = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest( project=instance_ref.project, instance=instance_ref.instance)) cache = remote_completion.RemoteCompletion() cache.AddToCache(instance_ref.SelfLink()) return new_resource except apitools_exceptions.HttpError as error: log.debug('operation : %s', str(operation_ref)) exc = exceptions.HttpException(error) if resource_property.Get(exc.payload.content, resource_lex.ParseKey('error.errors[0].reason'), None) == 'errorMaxInstancePerLabel': msg = resource_property.Get(exc.payload.content, resource_lex.ParseKey('error.message'), None) raise exceptions.HttpException(msg) raise
def _WhitelistClientIP(instance_ref, sql_client, sql_messages, resources, minutes=5): """Add CLIENT_IP to the authorized networks list. Makes an API call to add CLIENT_IP to the authorized networks list. The server knows to interpret the string CLIENT_IP as the address with which the client reaches the server. This IP will be whitelisted for 1 minute. Args: instance_ref: resources.Resource, The instance we're connecting to. sql_client: apitools.BaseApiClient, A working client for the sql version to be used. sql_messages: module, The module that defines the messages for the sql version to be used. resources: resources.Registry, The registry that can create resource refs for the sql version to be used. minutes: How long the client IP will be whitelisted for, in minutes. Returns: string, The name of the authorized network rule. Callers can use this name to find out the IP the client reached the server with. Raises: HttpException: An http error response was received while executing api request. ResourceNotFoundError: The SQL instance was not found. """ time_of_connection = network.GetCurrentTime() acl_name = 'sql connect at time {0}'.format(time_of_connection) user_acl = sql_messages.AclEntry( name=acl_name, expirationTime=iso_duration.Duration( minutes=minutes).GetRelativeDateTime(time_of_connection), value='CLIENT_IP') try: original = sql_client.instances.Get( sql_messages.SqlInstancesGetRequest( project=instance_ref.project, instance=instance_ref.instance)) except apitools_exceptions.HttpError as error: if error.status_code == httplib.FORBIDDEN: raise exceptions.ResourceNotFoundError( 'There was no instance found at {} or you are not authorized to ' 'connect to it.'.format(instance_ref.RelativeName())) raise calliope_exceptions.HttpException(error) original.settings.ipConfiguration.authorizedNetworks.append(user_acl) try: patch_request = sql_messages.SqlInstancesPatchRequest( databaseInstance=original, project=instance_ref.project, instance=instance_ref.instance) result = sql_client.instances.Patch(patch_request) except apitools_exceptions.HttpError as error: raise calliope_exceptions.HttpException(error) operation_ref = resources.Create('sql.operations', operation=result.name, project=instance_ref.project) message = ('Whitelisting your IP for incoming connection for ' '{0} {1}'.format(minutes, text.Pluralize(minutes, 'minute'))) operations.OperationsV1Beta4.WaitForOperation(sql_client, operation_ref, message) return acl_name
def Run(self, args): """Run 'deployments update'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns a struct containing the list of resources and list of outputs in the deployment. Raises: HttpException: An http error response was received while executing api request. """ deployment_ref = self.resources.Parse( args.deployment_name, params={'project': properties.VALUES.core.project.GetOrFail}, collection='deploymentmanager.deployments') if not args.IsSpecified('format') and args.async: args.format = flags.OPERATION_FORMAT patch_request = False deployment = self.messages.Deployment( name=deployment_ref.deployment, ) if not (args.config is None and args.template is None and args.composite_type is None): deployment.target = importer.BuildTargetConfig( self.messages, config=args.config, template=args.template, composite_type=args.composite_type, properties=args.properties) elif (self.ReleaseTrack() in [base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA] and args.manifest_id): deployment.target = importer.BuildTargetConfigFromManifest( self.client, self.messages, dm_base.GetProject(), deployment_ref.deployment, args.manifest_id, args.properties) # Get the fingerprint from the deployment to update. try: current_deployment = self.client.deployments.Get( self.messages.DeploymentmanagerDeploymentsGetRequest( project=dm_base.GetProject(), deployment=deployment_ref.deployment ) ) if args.fingerprint: deployment.fingerprint = dm_util.DecodeFingerprint(args.fingerprint) else: # If no fingerprint is present, default to an empty fingerprint. # TODO(b/34966984): Remove the empty default after cleaning up all # deployments that has no fingerprint deployment.fingerprint = current_deployment.fingerprint or '' # Get the credential from the deployment to update. if self.ReleaseTrack() in [base.ReleaseTrack.ALPHA] and args.credential: deployment.credential = dm_util.CredentialFrom(self.messages, args.credential) # Update the labels of the deployment deployment.labels = self._GetUpdatedDeploymentLabels( args, current_deployment) # If no config or manifest_id are specified, but try to update labels, # only add patch_request header when directly updating a non-previewed # deployment no_manifest = (self.ReleaseTrack() is base.ReleaseTrack.GA) or not args.manifest_id patch_request = not args.config and no_manifest and ( bool(args.update_labels) or bool(args.remove_labels)) if args.description is None: deployment.description = current_deployment.description elif not args.description or args.description.isspace(): deployment.description = None else: deployment.description = args.description except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) if patch_request: args.format = flags.DEPLOYMENT_FORMAT try: # Necessary to handle API Version abstraction below parsed_delete_flag = Update._delete_policy_flag_map.GetEnumForChoice( args.delete_policy).name parsed_create_flag = Update._create_policy_flag_map.GetEnumForChoice( args.create_policy).name request = self.messages.DeploymentmanagerDeploymentsUpdateRequest( deploymentResource=deployment, project=dm_base.GetProject(), deployment=deployment_ref.deployment, preview=args.preview, createPolicy=(self.messages.DeploymentmanagerDeploymentsUpdateRequest. CreatePolicyValueValuesEnum(parsed_create_flag)), deletePolicy=(self.messages.DeploymentmanagerDeploymentsUpdateRequest. DeletePolicyValueValuesEnum(parsed_delete_flag))) client = self.client client.additional_http_headers['X-Cloud-DM-Patch'] = patch_request operation = client.deployments.Update(request) # Fetch and print the latest fingerprint of the deployment. updated_deployment = dm_api_util.FetchDeployment( self.client, self.messages, dm_base.GetProject(), deployment_ref.deployment) if patch_request: if args.async: log.warn('Updating Deployment metadata is synchronous, --async flag ' 'is ignored.') log.status.Print('Update deployment metadata completed successfully.') return updated_deployment dm_util.PrintFingerprint(updated_deployment.fingerprint) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) if args.async: return operation else: op_name = operation.name try: dm_write.WaitForOperation(self.client, self.messages, op_name, 'update', dm_base.GetProject(), timeout=OPERATION_TIMEOUT) log.status.Print('Update operation ' + op_name + ' completed successfully.') except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) return dm_api_util.FetchResourcesAndOutputs( self.client, self.messages, dm_base.GetProject(), deployment_ref.deployment, self.ReleaseTrack() is base.ReleaseTrack.ALPHA)
def CatchHTTPErrorRaiseHTTPExceptionFn(*args, **kwargs): try: return func(*args, **kwargs) except apitools_exceptions.HttpError as error: core_exceptions.reraise( base_exceptions.HttpException(GetHttpErrorMessage(error)))
def Run(self, args): """Run 'deployments create'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns a struct containing the list of resources and list of outputs in the deployment. Raises: HttpException: An http error response was received while executing api request. ConfigError: Config file could not be read or parsed, or the deployment creation operation encountered an error. """ if ((not args.IsSpecified('format')) and (args. async or getattr(args, 'automatic_rollback', False))): args.format = flags.OPERATION_FORMAT deployment = self.messages.Deployment( name=args. deployment_name, # TODO(b/37913150): Use resource parser. target=importer.BuildTargetConfig(self.messages, args.config, args.properties), ) self._SetMetadata(args, deployment) try: operation = self.client.deployments.Insert( self.messages.DeploymentmanagerDeploymentsInsertRequest( project=dm_base.GetProject(), deployment=deployment, preview=args.preview, )) # Fetch and print the latest fingerprint of the deployment. fingerprint = dm_api_util.FetchDeploymentFingerprint( self.client, self.messages, dm_base.GetProject(), args.deployment_name) dm_util.PrintFingerprint(fingerprint) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) if args. async: return operation else: op_name = operation.name try: dm_write.WaitForOperation(self.client, self.messages, op_name, operation_description='create', project=dm_base.GetProject(), timeout=OPERATION_TIMEOUT) log.status.Print('Create operation ' + op_name + ' completed successfully.') except apitools_exceptions.HttpError as error: # TODO(b/37911296): Use gcloud default error handling. raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) except dm_exceptions.OperationError as error: response = self._HandleOperationError(error, args, operation, dm_base.GetProject()) return response return dm_api_util.FetchResourcesAndOutputs( self.client, self.messages, dm_base.GetProject(), args.deployment_name)
def Run(self, args): api_version = 'v1' # If in the future there are differences between API version, do NOT use # this patter of checking ReleaseTrack. Break this into multiple classes. if self.ReleaseTrack() == base.ReleaseTrack.BETA: api_version = 'v1beta2' elif self.ReleaseTrack() == base.ReleaseTrack.ALPHA: api_version = 'v1alpha2' if not os.path.exists(args.records_file): raise import_util.RecordsFileNotFound( 'Specified record file [{0}] not found.'.format(args.records_file)) if os.path.isdir(args.records_file): raise import_util.RecordsFileIsADirectory( 'Specified record file [{0}] is a directory'.format( args.records_file)) dns = util.GetApiClient(api_version) # Get the managed-zone. zone_ref = util.GetRegistry(api_version).Parse( args.zone, params={ 'project': properties.VALUES.core.project.GetOrFail, }, collection='dns.managedZones') try: zone = dns.managedZones.Get( dns.MESSAGES_MODULE.DnsManagedZonesGetRequest( project=zone_ref.project, managedZone=zone_ref.managedZone)) except apitools_exceptions.HttpError as error: raise calliope_exceptions.HttpException(error) # Get the current record-sets. current = {} for record in list_pager.YieldFromList( dns.resourceRecordSets, dns.MESSAGES_MODULE.DnsResourceRecordSetsListRequest( project=zone_ref.project, managedZone=zone_ref.Name()), field='rrsets'): current[(record.name, record.type)] = record # Get the imported record-sets. try: with files.FileReader(args.records_file) as import_file: if args.zone_file_format: imported = import_util.RecordSetsFromZoneFile( import_file, zone.dnsName, api_version=api_version) else: imported = import_util.RecordSetsFromYamlFile( import_file, api_version=api_version) except Exception as exp: msg = ('Unable to read record-sets from specified records-file [{0}] ' 'because [{1}]') msg = msg.format(args.records_file, exp.message) raise import_util.UnableToReadRecordsFile(msg) # Get the change resulting from the imported record-sets. change = import_util.ComputeChange(current, imported, args.delete_all_existing, zone.dnsName, args.replace_origin_ns, api_version=api_version) if not change: msg = 'Nothing to do, all the records in [{0}] already exist.'.format( args.records_file) log.status.Print(msg) return None # Send the change to the service. result = dns.changes.Create( dns.MESSAGES_MODULE.DnsChangesCreateRequest( change=change, managedZone=zone.name, project=zone_ref.project)) change_ref = util.GetRegistry(api_version).Create( collection='dns.changes', project=zone_ref.project, managedZone=zone.name, changeId=result.id) msg = 'Imported record-sets from [{0}] into managed-zone [{1}].'.format( args.records_file, zone_ref.Name()) log.status.Print(msg) log.CreatedResource(change_ref) return result
def Func(*args, **kwargs): try: return f(*args, **kwargs) except api_ex.HttpError as e: raise sdk_ex.HttpException( json.loads(e.content)['error']['message'])
def Run(self, args): """Run 'deployments create'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns a struct containing the list of resources and list of outputs in the deployment. Raises: HttpException: An http error response was received while executing api request. ConfigError: Config file could not be read or parsed, or the deployment creation operation encountered an error. """ deployment_ref = self.resources.Parse( args.deployment_name, params={'project': properties.VALUES.core.project.GetOrFail}, collection='deploymentmanager.deployments') if (not args.IsSpecified('format')) and (args. async): args.format = flags.OPERATION_FORMAT deployment = self.messages.Deployment( name=deployment_ref.deployment, target=importer.BuildTargetConfig( self.messages, config=args.config, template=args.template, composite_type=args.composite_type, properties=args.properties)) self._SetMetadata(args, deployment) try: operation = self.client.deployments.Insert( self._BuildRequest(args=args, project=dm_base.GetProject(), deployment=deployment)) # Fetch and print the latest fingerprint of the deployment. fingerprint = dm_api_util.FetchDeploymentFingerprint( self.client, self.messages, dm_base.GetProject(), deployment_ref.deployment) dm_util.PrintFingerprint(fingerprint) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) if args. async: return operation else: op_name = operation.name try: operation = dm_write.WaitForOperation( self.client, self.messages, op_name, operation_description='create', project=dm_base.GetProject(), timeout=OPERATION_TIMEOUT) dm_util.LogOperationStatus(operation, 'Create') except apitools_exceptions.HttpError as error: # TODO(b/37911296): Use gcloud default error handling. raise exceptions.HttpException(error, dm_api_util.HTTP_ERROR_FORMAT) except dm_exceptions.OperationError as error: response = self._HandleOperationError(error, args, operation, dm_base.GetProject(), deployment_ref) if getattr(args, 'automatic_rollback', False): args.format = flags.OPERATION_FORMAT return response return dm_api_util.FetchResourcesAndOutputs( self.client, self.messages, dm_base.GetProject(), deployment_ref.deployment, self.ReleaseTrack() is base.ReleaseTrack.ALPHA)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) # Make sure it exists (will raise appropriate error if not) cluster = adapter.GetCluster(cluster_ref) # locations will be None if additional-zones was specified, an empty list # if it was specified with no argument, or a populated list if zones were # provided. We want to distinguish between the case where it isn't # specified (and thus shouldn't be passed on to the API) and the case where # it's specified as wanting no additional zones, in which case we must pass # the cluster's primary zone to the API. # TODO(b/29578401): Remove the hasattr once the flag is GA. locations = None if hasattr(args, 'additional_zones') and args.additional_zones is not None: locations = sorted([cluster_ref.zone] + args.additional_zones) if hasattr(args, 'node_locations') and args.node_locations is not None: locations = sorted(args.node_locations) if args.username is not None or args.enable_basic_auth is not None: flags.MungeBasicAuthFlags(args) options = api_adapter.SetMasterAuthOptions( action=api_adapter.SetMasterAuthOptions.SET_USERNAME, username=args.username, password=args.password) try: op_ref = adapter.SetMasterAuth(cluster_ref, options) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif (args.generate_password or args.set_password or args.password is not None): if args.generate_password: password = '' options = api_adapter.SetMasterAuthOptions( action=api_adapter.SetMasterAuthOptions.GENERATE_PASSWORD, password=password) else: password = args.password if args.password is None: password = raw_input('Please enter the new password:'******'Enabling/Disabling Network Policy causes a rolling ' 'update of all cluster nodes, similar to performing a cluster ' 'upgrade. This operation is long-running and will block other ' 'operations on the cluster (including delete) until it has run ' 'to completion.', cancel_on_no=True) options = api_adapter.SetNetworkPolicyOptions( enabled=args.enable_network_policy) try: op_ref = adapter.SetNetworkPolicy(cluster_ref, options) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.start_ip_rotation: console_io.PromptContinue( message= 'This will start an IP Rotation on cluster [{name}]. The ' 'master will be updated to serve on a new IP address in addition to ' 'the current IP address. Kubernetes Engine will then recreate all ' 'nodes ({num_nodes} nodes) to point to the new IP address. This ' 'operation is long-running and will block other operations on the ' 'cluster (including delete) until it has run to completion.'. format(name=cluster.name, num_nodes=cluster.currentNodeCount), cancel_on_no=True) try: op_ref = adapter.StartIpRotation(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.complete_ip_rotation: console_io.PromptContinue( message= 'This will complete the in-progress IP Rotation on cluster ' '[{name}]. The master will be updated to stop serving on the old IP ' 'address and only serve on the new IP address. Make sure all API ' 'clients have been updated to communicate with the new IP address ' '(e.g. by running `gcloud container clusters get-credentials ' '--project {project} --zone {zone} {name}`). This operation is long-' 'running and will block other operations on the cluster (including ' 'delete) until it has run to completion.'.format( name=cluster.name, project=cluster_ref.projectId, zone=cluster.zone), cancel_on_no=True) try: op_ref = adapter.CompleteIpRotation(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.update_labels is not None: try: op_ref = adapter.UpdateLabels(cluster_ref, args.update_labels) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.remove_labels is not None: try: op_ref = adapter.RemoveLabels(cluster_ref, args.remove_labels) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.logging_service is not None: try: op_ref = adapter.SetLoggingService(cluster_ref, args.logging_service) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.maintenance_window is not None: try: op_ref = adapter.SetMaintenanceWindow(cluster_ref, args.maintenance_window) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) else: if args.enable_legacy_authorization is not None: op_ref = adapter.SetLegacyAuthorization( cluster_ref, args.enable_legacy_authorization) else: options = self.ParseUpdateOptions(args, locations) op_ref = adapter.UpdateCluster(cluster_ref, options) if not args. async: adapter.WaitForOperation( op_ref, 'Updating {0}'.format(cluster_ref.clusterId)) log.UpdatedResource(cluster_ref) cluster_url = util.GenerateClusterUrl(cluster_ref) log.status.Print( 'To inspect the contents of your cluster, go to: ' + cluster_url) if args.start_ip_rotation or args.complete_ip_rotation: cluster = adapter.GetCluster(cluster_ref) try: util.ClusterConfig.Persist(cluster, cluster_ref.projectId) except kconfig.MissingEnvVarError as error: log.warning(error.message)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_refs = [] for name in args.names: cluster_refs.append(adapter.ParseCluster(name, location)) console_io.PromptContinue( message=util.ConstructList('The following clusters will be deleted.', [ '[{name}] in [{zone}]'.format( name=ref.clusterId, zone=adapter.Zone(ref)) for ref in cluster_refs ]), throw_if_unattended=True, cancel_on_no=True) operations = [] errors = [] # Issue all deletes first for cluster_ref in cluster_refs: try: op_ref = adapter.DeleteCluster(cluster_ref) operations.append((op_ref, cluster_ref)) except apitools_exceptions.HttpError as error: errors.append( six.text_type( exceptions.HttpException(error, util.HTTP_ERROR_FORMAT))) except util.Error as error: errors.append(error) if not args.async: # Poll each operation for completion for operation_ref, cluster_ref in operations: try: adapter.WaitForOperation( operation_ref, 'Deleting cluster {0}'.format(cluster_ref.clusterId), timeout_s=args.timeout) # Purge cached config files try: util.ClusterConfig.Purge(cluster_ref.clusterId, adapter.Zone(cluster_ref), cluster_ref.projectId) except kconfig.MissingEnvVarError as error: log.warning(error) if properties.VALUES.container.cluster.Get() == cluster_ref.clusterId: properties.PersistProperty(properties.VALUES.container.cluster, None) log.DeletedResource(cluster_ref) except apitools_exceptions.HttpError as error: errors.append(exceptions.HttpException(error, util.HTTP_ERROR_FORMAT)) except util.Error as error: errors.append(error) if errors: raise util.Error( util.ConstructList('Some requests did not succeed:', errors))
def Run(self, args): if not os.path.isfile(args.records_file): raise exceptions.ToolException('no such file [{0}]'.format( args.records_file)) dns = self.context['dns_client'] messages = self.context['dns_messages'] resources = self.context['dns_resources'] project_id = properties.VALUES.core.project.Get(required=True) # Get the managed-zone. zone_ref = resources.Parse(args.zone, collection='dns.managedZones') try: zone = dns.managedZones.Get(zone_ref.Request()) except apitools.HttpError as error: raise exceptions.HttpException(util.GetErrorMessage(error)) # Get the current record-sets. current = {} for record in apitools.YieldFromList( dns.resourceRecordSets, messages.DnsResourceRecordSetsListRequest( project=project_id, managedZone=zone_ref.Name()), field='rrsets'): current[(record.name, record.type)] = record # Get the imported record-sets. try: with files.Context(open(args.records_file)) as import_file: if args.zone_file_format: imported = import_util.RecordSetsFromZoneFile( import_file, zone.dnsName) else: imported = import_util.RecordSetsFromYamlFile(import_file) except Exception as exp: msg = ( 'unable to read record-sets from specified records-file [{0}] ' 'because [{1}]') msg = msg.format(args.records_file, exp.message) raise exceptions.ToolException(msg) # Get the change resulting from the imported record-sets. change = import_util.ComputeChange(current, imported, args.delete_all_existing) if not change: msg = 'Nothing to do, all the records in [{0}] already exist.'.format( args.records_file) log.status.Print(msg) return None # Send the change to the service. result = dns.changes.Create( messages.DnsChangesCreateRequest(change=change, managedZone=zone.name, project=project_id)) change_ref = resources.Create(collection='dns.changes', project=project_id, managedZone=zone.name, changeId=result.id) msg = 'Imported record-sets from [{0}] into managed-zone [{1}].'.format( args.records_file, zone_ref.Name()) log.status.Print(msg) log.CreatedResource(change_ref) return result
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) project_id = properties.VALUES.core.project.Get(required=True) concurrent_node_count = getattr(args, 'concurrent_node_count', None) try: cluster = adapter.GetCluster(cluster_ref) except (exceptions.HttpException, apitools_exceptions.HttpForbiddenError, util.Error) as error: log.warning(('Problem loading details of cluster to upgrade:\n\n{}\n\n' 'You can still attempt to upgrade the cluster.\n') .format(console_attr.SafeText(error))) cluster = None try: server_conf = adapter.GetServerConfig(project_id, location) except (exceptions.HttpException, apitools_exceptions.HttpForbiddenError, util.Error) as error: log.warning(('Problem loading server config:\n\n{}\n\n' 'You can still attempt to upgrade the cluster.\n') .format(console_attr.SafeText(error))) server_conf = None upgrade_message = container_command_util.ClusterUpgradeMessage( name=args.name, server_conf=server_conf, cluster=cluster, master=args.master, node_pool_name=args.node_pool, new_version=args.cluster_version, concurrent_node_count=concurrent_node_count) console_io.PromptContinue( message=upgrade_message, throw_if_unattended=True, cancel_on_no=True) options = self.ParseUpgradeOptions(args) try: op_ref = adapter.UpdateCluster(cluster_ref, options) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) if not args.async: adapter.WaitForOperation( op_ref, 'Upgrading {0}'.format(cluster_ref.clusterId), timeout_s=args.timeout) log.UpdatedResource(cluster_ref)
def Run(self, args): """Run 'deployments cancel'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns boolean indicating whether cancel operation succeeded. Raises: HttpException: An http error response was received while executing api request. ToolException: The cancel operation encountered an error. """ client = self.context['deploymentmanager-v2beta2'] messages = self.context['deploymentmanager-v2beta2-messages'] project = properties.VALUES.core.project.Get(required=True) deployment = messages.Deployment( name=args.deployment_name, intent='CANCEL', ) # Get the fingerprint from the deployment to update. try: current_deployment = client.deployments.Get( messages.DeploymentmanagerDeploymentsGetRequest( project=project, deployment=args.deployment_name)) # If no fingerprint is present, default to an empty fingerprint. # This empty default can be removed once the fingerprint change is # fully implemented and all deployments have fingerprints. deployment.fingerprint = current_deployment.fingerprint or '' except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) try: operation = client.deployments.Update( messages.DeploymentmanagerDeploymentsUpdateRequest( deploymentResource=deployment, project=project, deployment=args.deployment_name, )) except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) if args. async: return operation else: op_name = operation.name try: dm_v2_util.WaitForOperation(op_name, project, self.context, 'cancel', OPERATION_TIMEOUT) log.status.Print('Cancel operation ' + op_name + ' completed successfully.') except exceptions.ToolException: # Operation timed out or had errors. Print this warning, then still # show whatever operation can be gotten. log.error('Cancel operation ' + op_name + ' has errors or failed to complete within ' + str(OPERATION_TIMEOUT) + ' seconds.') except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error)) try: # Fetch a list of the canceled resources. response = client.resources.List( messages.DeploymentmanagerResourcesListRequest( project=project, deployment=args.deployment_name, )) # TODO(user): Pagination return response.resources if response.resources else [] except apitools_base.HttpError as error: raise exceptions.HttpException(dm_v2_util.GetError(error))
class Create(base.CreateCommand): """Create a cluster for running containers.""" @staticmethod def Args(parser): _Args(parser) flags.AddClusterAutoscalingFlags(parser, suppressed=True) flags.AddLocalSSDFlag(parser, suppressed=True) flags.AddEnableKubernetesAlphaFlag(parser, suppressed=True) flags.AddClusterVersionFlag(parser, 'master and nodes', True) flags.AddPreemptibleFlag(parser, suppressed=True) flags.AddEnableAutoRepairFlag(parser, suppressed=True) flags.AddEnableAutoUpgradeFlag(parser, suppressed=True) def ParseCreateOptions(self, args): if not args.scopes: args.scopes = [] cluster_ipv4_cidr = args.cluster_ipv4_cidr return api_adapter.CreateClusterOptions( node_machine_type=args.machine_type, scopes=args.scopes, enable_cloud_endpoints=args.enable_cloud_endpoints, num_nodes=args.num_nodes, additional_zones=args.additional_zones, user=args.username, password=args.password, cluster_version=args.cluster_version, network=args.network, subnetwork=args.subnetwork, cluster_ipv4_cidr=cluster_ipv4_cidr, node_disk_size_gb=args.disk_size, enable_cloud_logging=args.enable_cloud_logging, enable_cloud_monitoring=args.enable_cloud_monitoring, enable_kubernetes_alpha=args.enable_kubernetes_alpha, disable_addons=args.disable_addons, local_ssd_count=args.local_ssd_count, tags=args.tags, node_labels=args.node_labels, enable_autoscaling=args.enable_autoscaling, max_nodes=args.max_nodes, min_nodes=args.min_nodes, image_type=args.image_type, max_nodes_per_pool=args.max_nodes_per_pool, preemptible=args.preemptible, enable_autorepair=args.enable_autorepair, enable_autoupgrade=args.enable_autoupgrade) def Collection(self): return 'container.projects.zones.clusters' def Format(self, args): return self.ListFormat(args) def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: util.Error, if creation failed. """ util.CheckKubectlInstalled() adapter = self.context['api_adapter'] if not args.scopes: args.scopes = [] cluster_ref = adapter.ParseCluster(args.name) options = self.ParseCreateOptions(args) if options.enable_kubernetes_alpha: console_io.PromptContinue( message=constants.KUBERNETES_ALPHA_PROMPT, throw_if_unattended=True, cancel_on_no=True) if options.enable_autorepair is not None: log.status.Print( messages.AutoUpdateUpgradeRepairMessage( options.enable_autorepair, 'autorepair')) if options.enable_autoupgrade is not None: log.status.Print( messages.AutoUpdateUpgradeRepairMessage( options.enable_autoupgrade, 'autoupgrade')) operation = None try: operation_ref = adapter.CreateCluster(cluster_ref, options) if flags.GetAsyncValueFromAsyncAndWaitFlags( args. async, args.wait): return adapter.GetCluster(cluster_ref) operation = adapter.WaitForOperation(operation_ref, 'Creating cluster {0}'.format( cluster_ref.clusterId), timeout_s=args.timeout) cluster = adapter.GetCluster(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT)
class Create(base.CreateCommand): """Create a cluster for running containers.""" @staticmethod def Args(parser): _Args(parser) _AddAdditionalZonesFlag(parser, deprecated=True) flags.AddNodeLocationsFlag(parser) flags.AddAddonsFlags(parser) flags.AddClusterAutoscalingFlags(parser) flags.AddEnableAutoRepairFlag(parser, for_create=True) flags.AddEnableKubernetesAlphaFlag(parser) flags.AddEnableLegacyAuthorizationFlag(parser) flags.AddIPAliasFlags(parser) flags.AddLabelsFlag(parser) flags.AddLocalSSDFlag(parser) flags.AddMaintenanceWindowFlag(parser) flags.AddMasterAuthorizedNetworksFlags(parser) flags.AddMinCpuPlatformFlag(parser) flags.AddNetworkPolicyFlags(parser) flags.AddNodeTaintsFlag(parser) flags.AddPreemptibleFlag(parser) flags.AddDeprecatedClusterNodeIdentityFlags(parser) flags.AddPrivateClusterFlags(parser, with_deprecated=False) def ParseCreateOptions(self, args): return ParseCreateOptionsBase(args) def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Cluster message for the successfully created cluster. Raises: util.Error, if creation failed. """ if args. async and not args.IsSpecified('format'): args.format = util.OPERATIONS_FORMAT util.CheckKubectlInstalled() adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) options = self.ParseCreateOptions(args) if options.private_cluster and not ( options.enable_master_authorized_networks or options.master_authorized_networks): log.warning( '`--private-cluster` makes the master inaccessible from ' 'cluster-external IP addresses, by design. To allow limited ' 'access to the master, see the `--master-authorized-networks` flags ' 'and our documentation on setting up private clusters: ' 'https://cloud.google.com' '/kubernetes-engine/docs/how-to/private-clusters') if not (options.metadata and 'disable-legacy-endpoints' in options.metadata): log.warning( 'Starting in 1.12, default node pools in new clusters ' 'will have their legacy Compute Engine instance metadata ' 'endpoints disabled by default. To create a cluster with ' 'legacy instance metadata endpoints disabled in the default ' 'node pool, run `clusters create` with the flag ' '`--metadata disable-legacy-endpoints=true`.') if options.enable_kubernetes_alpha: console_io.PromptContinue( message=constants.KUBERNETES_ALPHA_PROMPT, throw_if_unattended=True, cancel_on_no=True) if options.enable_autorepair is not None: log.status.Print( messages.AutoUpdateUpgradeRepairMessage( options.enable_autorepair, 'autorepair')) if options.enable_autoupgrade is not None: log.status.Print( messages.AutoUpdateUpgradeRepairMessage( options.enable_autoupgrade, 'autoupgrade')) if options.accelerators is not None: log.status.Print(constants.KUBERNETES_GPU_LIMITATION_MSG) operation = None try: operation_ref = adapter.CreateCluster(cluster_ref, options) if args. async: return adapter.GetCluster(cluster_ref) operation = adapter.WaitForOperation( operation_ref, 'Creating cluster {0} in {1}'.format(cluster_ref.clusterId, cluster_ref.zone), timeout_s=args.timeout) cluster = adapter.GetCluster(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) log.CreatedResource(cluster_ref) cluster_url = util.GenerateClusterUrl(cluster_ref) log.status.Print('To inspect the contents of your cluster, go to: ' + cluster_url) if operation.detail: # Non-empty detail on a DONE create operation should be surfaced as # a warning to end user. log.warning(operation.detail) try: util.ClusterConfig.Persist(cluster, cluster_ref.projectId) except kconfig.MissingEnvVarError as error: log.warning(error) return [cluster]
def Run(self, args): """Run 'deployments create'. Args: args: argparse.Namespace, The arguments that this command was invoked with. Returns: If --async=true, returns Operation to poll. Else, returns a struct containing the list of resources and list of outputs in the deployment. Raises: HttpException: An http error response was received while executing api request. ConfigError: Config file could not be read or parsed, or the deployment creation operation encountered an error. """ deployment = dm_base.GetMessages().Deployment( name=args.deployment_name, target=importer.BuildTargetConfig(dm_base.GetMessages(), args.config, args.properties), ) if self.ReleaseTrack() in [base.ReleaseTrack.ALPHA]: label_dict = labels_util.GetUpdateLabelsDictFromArgs(args) label_entry = [] if label_dict: label_entry = [ dm_base.GetMessages().DeploymentLabelEntry(key=k, value=v) for k, v in sorted(label_dict.iteritems()) ] deployment.labels = label_entry if args.description: deployment.description = args.description try: operation = dm_base.GetClient().deployments.Insert( dm_base.GetMessages( ).DeploymentmanagerDeploymentsInsertRequest( project=dm_base.GetProject(), deployment=deployment, preview=args.preview, )) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_v2_util.HTTP_ERROR_FORMAT) if args. async: return operation else: op_name = operation.name try: dm_write.WaitForOperation(op_name, operation_description='create', project=dm_base.GetProject(), timeout=OPERATION_TIMEOUT) log.status.Print('Create operation ' + op_name + ' completed successfully.') except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, dm_v2_util.HTTP_ERROR_FORMAT) return dm_v2_util.FetchResourcesAndOutputs(dm_base.GetClient(), dm_base.GetMessages(), dm_base.GetProject(), args.deployment_name)
def Run(self, args): """This is what gets called when the user runs this command. Args: args: an argparse namespace. All the arguments that were provided to this command invocation. Returns: Some value that we want to have printed later. """ adapter = self.context['api_adapter'] location_get = self.context['location_get'] location = location_get(args) cluster_ref = adapter.ParseCluster(args.name, location) cluster_name = args.name cluster_node_count = None cluster_zone = cluster_ref.zone cluster_is_required = self.IsClusterRequired(args) try: # Attempt to get cluster for better prompts and to validate args. # Error is a warning but not fatal. Should only exit with a failure on # the actual update API calls below. cluster = adapter.GetCluster(cluster_ref) cluster_name = cluster.name cluster_node_count = cluster.currentNodeCount cluster_zone = cluster.zone except (exceptions.HttpException, apitools_exceptions.HttpForbiddenError, util.Error) as error: if cluster_is_required: raise log.warning( ('Problem loading details of cluster to update:\n\n{}\n\n' 'You can still attempt updates to the cluster.\n').format( console_attr.SafeText(error))) # locations will be None if additional-zones was specified, an empty list # if it was specified with no argument, or a populated list if zones were # provided. We want to distinguish between the case where it isn't # specified (and thus shouldn't be passed on to the API) and the case where # it's specified as wanting no additional zones, in which case we must pass # the cluster's primary zone to the API. # TODO(b/29578401): Remove the hasattr once the flag is GA. locations = None if hasattr(args, 'additional_zones') and args.additional_zones is not None: locations = sorted([cluster_ref.zone] + args.additional_zones) if hasattr(args, 'node_locations') and args.node_locations is not None: locations = sorted(args.node_locations) if args.IsSpecified('username') or args.IsSpecified( 'enable_basic_auth'): flags.MungeBasicAuthFlags(args) options = api_adapter.SetMasterAuthOptions( action=api_adapter.SetMasterAuthOptions.SET_USERNAME, username=args.username, password=args.password) try: op_ref = adapter.SetMasterAuth(cluster_ref, options) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif (args.generate_password or args.set_password or args.IsSpecified('password')): if args.generate_password: password = '' options = api_adapter.SetMasterAuthOptions( action=api_adapter.SetMasterAuthOptions.GENERATE_PASSWORD, password=password) else: password = args.password if not args.IsSpecified('password'): password = input('Please enter the new password:'******'Enabling/Disabling Network Policy causes a rolling ' 'update of all cluster nodes, similar to performing a cluster ' 'upgrade. This operation is long-running and will block other ' 'operations on the cluster (including delete) until it has run ' 'to completion.', cancel_on_no=True) options = api_adapter.SetNetworkPolicyOptions( enabled=args.enable_network_policy) try: op_ref = adapter.SetNetworkPolicy(cluster_ref, options) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.start_ip_rotation or args.start_credential_rotation: if args.start_ip_rotation: msg_tmpl = """This will start an IP Rotation on cluster [{name}]. The \ master will be updated to serve on a new IP address in addition to the current \ IP address. Kubernetes Engine will then recreate all nodes ({num_nodes} nodes) \ to point to the new IP address. This operation is long-running and will block \ other operations on the cluster (including delete) until it has run to \ completion.""" rotate_credentials = False elif args.start_credential_rotation: msg_tmpl = """This will start an IP and Credentials Rotation on cluster\ [{name}]. The master will be updated to serve on a new IP address in addition \ to the current IP address, and cluster credentials will be rotated. Kubernetes \ Engine will then recreate all nodes ({num_nodes} nodes) to point to the new IP \ address. This operation is long-running and will block other operations on the \ cluster (including delete) until it has run to completion.""" rotate_credentials = True console_io.PromptContinue(message=msg_tmpl.format( name=cluster_name, num_nodes=cluster_node_count if cluster_node_count else '?'), cancel_on_no=True) try: op_ref = adapter.StartIpRotation( cluster_ref, rotate_credentials=rotate_credentials) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.complete_ip_rotation or args.complete_credential_rotation: if args.complete_ip_rotation: msg_tmpl = """This will complete the in-progress IP Rotation on \ cluster [{name}]. The master will be updated to stop serving on the old IP \ address and only serve on the new IP address. Make sure all API clients have \ been updated to communicate with the new IP address (e.g. by running `gcloud \ container clusters get-credentials --project {project} --zone {zone} {name}`). \ This operation is long-running and will block other operations on the cluster \ (including delete) until it has run to completion.""" elif args.complete_credential_rotation: msg_tmpl = """This will complete the in-progress Credential Rotation on\ cluster [{name}]. The master will be updated to stop serving on the old IP \ address and only serve on the new IP address. Old cluster credentials will be \ invalidated. Make sure all API clients have been updated to communicate with \ the new IP address (e.g. by running `gcloud container clusters get-credentials \ --project {project} --zone {zone} {name}`). This operation is long-running and \ will block other operations on the cluster (including delete) until it has run \ to completion.""" console_io.PromptContinue(message=msg_tmpl.format( name=cluster_name, project=cluster_ref.projectId, zone=cluster_zone), cancel_on_no=True) try: op_ref = adapter.CompleteIpRotation(cluster_ref) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.update_labels is not None: try: op_ref = adapter.UpdateLabels(cluster_ref, args.update_labels) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.remove_labels is not None: try: op_ref = adapter.RemoveLabels(cluster_ref, args.remove_labels) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.logging_service is not None and args.monitoring_service is None: try: op_ref = adapter.SetLoggingService(cluster_ref, args.logging_service) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif args.maintenance_window is not None: try: op_ref = adapter.SetDailyMaintenanceWindow( cluster_ref, cluster.maintenancePolicy, args.maintenance_window) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif getattr(args, 'maintenance_window_start', None) is not None: try: op_ref = adapter.SetRecurringMaintenanceWindow( cluster_ref, cluster.maintenancePolicy, args.maintenance_window_start, args.maintenance_window_end, args.maintenance_window_recurrence) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif getattr(args, 'clear_maintenance_window', None): try: op_ref = adapter.RemoveMaintenanceWindow( cluster_ref, cluster.maintenancePolicy) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif getattr(args, 'add_maintenance_exclusion_end', None) is not None: try: op_ref = adapter.AddMaintenanceExclusion( cluster_ref, cluster.maintenancePolicy, args.add_maintenance_exclusion_name, args.add_maintenance_exclusion_start, args.add_maintenance_exclusion_end) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) elif getattr(args, 'remove_maintenance_exclusion', None) is not None: try: op_ref = adapter.RemoveMaintenanceExclusion( cluster_ref, cluster.maintenancePolicy, args.remove_maintenance_exclusion) except apitools_exceptions.HttpError as error: raise exceptions.HttpException(error, util.HTTP_ERROR_FORMAT) else: if args.enable_legacy_authorization is not None: op_ref = adapter.SetLegacyAuthorization( cluster_ref, args.enable_legacy_authorization) else: options = self.ParseUpdateOptions(args, locations) op_ref = adapter.UpdateCluster(cluster_ref, options) if not args.async_: adapter.WaitForOperation(op_ref, 'Updating {0}'.format( cluster_ref.clusterId), timeout_s=3600) log.UpdatedResource(cluster_ref) cluster_url = util.GenerateClusterUrl(cluster_ref) log.status.Print( 'To inspect the contents of your cluster, go to: ' + cluster_url) if (args.start_ip_rotation or args.complete_ip_rotation or args.start_credential_rotation or args.complete_credential_rotation): cluster = adapter.GetCluster(cluster_ref) try: util.ClusterConfig.Persist(cluster, cluster_ref.projectId) except kconfig.MissingEnvVarError as error: log.warning(error)
def _GetCloudDnsDetails(domains_messages, cloud_dns_zone, domain, enable_dnssec): """Fetches list of name servers from provided Cloud DNS Managed Zone. Args: domains_messages: Cloud Domains messages module. cloud_dns_zone: Cloud DNS Zone resource reference. domain: Domain name. enable_dnssec: If true, try to read DNSSEC information from the Zone. Returns: A pair: List of name servers and a list of Ds records (or [] if e.g. the Zone is not signed). """ # Get the managed-zone. dns_api_version = 'v1' dns = apis.GetClientInstance('dns', dns_api_version) dns_messages = dns.MESSAGES_MODULE zone_ref = dns_api_util.GetRegistry(dns_api_version).Parse( cloud_dns_zone, params={ 'project': properties.VALUES.core.project.GetOrFail, }, collection='dns.managedZones') try: zone = dns.managedZones.Get( dns_messages.DnsManagedZonesGetRequest( project=zone_ref.project, managedZone=zone_ref.managedZone)) except apitools_exceptions.HttpError as error: raise calliope_exceptions.HttpException(error) domain_with_dot = domain + '.' if zone.dnsName != domain_with_dot: raise exceptions.Error( 'The dnsName \'{}\' of specified Cloud DNS zone \'{}\' does not match the ' 'registration domain \'{}\''.format(zone.dnsName, cloud_dns_zone, domain_with_dot)) if zone.visibility != dns_messages.ManagedZone.VisibilityValueValuesEnum.public: raise exceptions.Error( 'Cloud DNS Zone \'{}\' is not public.'.format(cloud_dns_zone)) if not enable_dnssec: return zone.nameServers, [] signed = dns_messages.ManagedZoneDnsSecConfig.StateValueValuesEnum.on if not zone.dnssecConfig or zone.dnssecConfig.state != signed: log.status.Print( 'Cloud DNS Zone \'{}\' is not signed. DNSSEC won\'t be enabled.'. format(cloud_dns_zone)) return zone.nameServers, [] try: dns_keys = [] req = dns_messages.DnsDnsKeysListRequest( project=zone_ref.project, managedZone=zone_ref.managedZone, maxResults=1) while True: resp = dns.dnsKeys.List( dns_messages.DnsDnsKeysListRequest( project=zone_ref.project, managedZone=zone_ref.managedZone)) dns_keys += resp.dnsKeys req.pageToken = resp.nextPageToken if not resp.nextPageToken: break except apitools_exceptions.HttpError as error: log.status.Print( 'Cannot read DS records from Cloud DNS Zone \'{}\': {}. ' 'DNSSEC won\'t be enabled.'.format(cloud_dns_zone, error)) ds_records = _ConvertDnsKeys(domains_messages, dns_messages, dns_keys) if not ds_records: log.status.Print( 'No supported DS records found in Cloud DNS Zone \'{}\'. ' 'DNSSEC won\'t be enabled.'.format(cloud_dns_zone)) return zone.nameServers, [] return zone.nameServers, ds_records