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,33 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Provide commands for managing Cloud SQL instances."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Instances(base.Group):
"""Provide commands for managing Cloud SQL instances.
Provide commands for managing Cloud SQL instances including creating,
configuring, restarting, and deleting instances.
"""
category = base.DATABASES_CATEGORY

View File

@@ -0,0 +1,118 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Acquires a SQL Server Reporting Services lease on a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
DESCRIPTION = """\
Acquire a SQL Server Reporting Services lease on a Cloud SQL instance.
"""
EXAMPLES = """\
To acquire a SQL Server Reporting Services lease on an instance:
$ {command} instance-foo --setup-login=setuplogin --service-login=reportuser --report-database=ReportServer --duration=4h
"""
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES,
}
@base.ReleaseTracks(base.ReleaseTrack.GA)
class AcquireSsrsLease(base.Command):
"""Acquires a SQL Server Reporting Services lease on a Cloud SQL instance."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
Returns:
A dict object representing the operations resource describing the acquire
SSRS lease operation if the operation was successful.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.',
)
flags.AddSqlServerSsrs(parser)
def Run(self, args):
"""Acquires a SQL Server Reporting Services lease on a 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
acquire-ssrs-lease operation if the acquire-ssrs-lease was successful.
"""
client = api_util.SqlClient(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')
duration_str = args.duration
if duration_str is not None:
duration_str = str(args.duration) + 's'
request = sql_messages.SqlInstancesAcquireSsrsLeaseRequest(
instance=instance_ref.instance,
project=instance_ref.project,
instancesAcquireSsrsLeaseRequest=sql_messages.InstancesAcquireSsrsLeaseRequest(
acquireSsrsLeaseContext=sql_messages.AcquireSsrsLeaseContext(
setupLogin=args.setup_login,
serviceLogin=args.service_login,
reportDatabase=args.report_database,
duration=duration_str,
),),
)
result = sql_client.instances.AcquireSsrsLease(request)
operation_ref = client.resource_parser.Create(
'sql.operations',
operation=result.operationId,
project=instance_ref.project)
operations.OperationsV1Beta4.WaitForOperation(sql_client, operation_ref,
'Acquiring SSRS lease')
log.status.write('Successfully acquired SSRS lease.\n')

View File

@@ -0,0 +1,363 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Clones a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.command_lib.sql import instances as command_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
DESCRIPTION = ("""\
*{command}* creates a clone of a Cloud SQL instance. The clone is an
independent copy of the source instance with the same data and settings.
Source and destination instances must be in the same project. An instance
can be cloned from its current state, or from an earlier point in time.
For MySQL: The binary log coordinates or timestamp (point in time), if
specified, act as the point in time the source instance is cloned from. If
not specified, the current state of the instance is cloned.
For PostgreSQL: The point in time, if specified, defines a past state of the
instance to clone. If not specified, the current state of the instance is
cloned.
For SQL Server: The point in time, if specified, defines a past state of the
instance to clone. If not specified, the current state of the instance is
cloned.
""")
EXAMPLES_GA = ("""\
To clone an instance from its current state (most recent binary log
coordinates):
$ {command} my-source-instance my-cloned-instance
To clone a MySQL instance from an earlier point in time (past binary log
coordinates):
$ {command} my-source-instance my-cloned-instance --bin-log-file-name mysql-bin.000020 --bin-log-position 170
To clone a MySQL source instance at a specific point in time:
$ {command} my-source-instance my-cloned-instance --point-in-time '2012-11-15T16:19:00.094Z'
To clone a PostgreSQL source instance at a specific point in time:
$ {command} my-source-instance my-cloned-instance --point-in-time '2012-11-15T16:19:00.094Z'
To clone a SQL Server source instance at a specific point in time:
$ {command} my-source-instance my-cloned-instance --point-in-time '2012-11-15T16:19:00.094Z'
To clone a deleted instance, include the name and deletion time of the source instance:
$ {command} my-source-instance my-cloned-instance --source-instance-deletion-time '2012-11-15T16:19:00.094Z'
""")
EXAMPLES_ALPHA = ("""\
To specify the allocated IP range for the private IP target Instance
(reserved for future use):
$ {command} my-source-instance my-cloned-instance --allocated-ip-range-name cloned-instance-ip-range
""")
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES_GA,
}
DETAILED_APLHA_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES_GA + EXAMPLES_ALPHA,
}
def _GetInstanceRefsFromArgs(args, client):
"""Get validated refs to source and destination instances from args."""
validate.ValidateInstanceName(args.source)
validate.ValidateInstanceName(args.destination)
source_instance_ref = client.resource_parser.Parse(
args.source,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances')
destination_instance_ref = client.resource_parser.Parse(
args.destination,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances')
_CheckSourceAndDestination(source_instance_ref, destination_instance_ref)
return source_instance_ref, destination_instance_ref
def _CheckSourceAndDestination(source_instance_ref, destination_instance_ref):
"""Verify that the source and destination instance ids are different."""
if source_instance_ref.project != destination_instance_ref.project:
raise exceptions.ArgumentError(
'The source and the clone instance must belong to the same project:'
' "{src}" != "{dest}".'.format(
src=source_instance_ref.project,
dest=destination_instance_ref.project))
def AddAlphaArgs(parser):
"""Declare alpha flags for this command parser."""
parser.add_argument(
'--allocated-ip-range-name',
required=False,
help="""\
The name of the IP range allocated for the destination instance with
private network connectivity. For example:
\'google-managed-services-default\'. If set, the destination instance
IP is created in the allocated range represented by this name.
Reserved for future use.
""")
def _UpdateRequestFromArgs(request, args, sql_messages, release_track):
"""Update request with clone options."""
clone_context = request.instancesCloneRequest.cloneContext
# PITR options
if args.bin_log_file_name and args.bin_log_position:
clone_context.binLogCoordinates = sql_messages.BinLogCoordinates(
binLogFileName=args.bin_log_file_name,
binLogPosition=args.bin_log_position)
elif args.point_in_time:
clone_context.pointInTime = args.point_in_time.strftime(
'%Y-%m-%dT%H:%M:%S.%fZ')
if args.point_in_time and args.restore_database_name:
clone_context.databaseNames[:] = [args.restore_database_name]
if args.preferred_zone:
clone_context.preferredZone = args.preferred_zone
if args.preferred_secondary_zone:
clone_context.preferredSecondaryZone = args.preferred_secondary_zone
if args.source_instance_deletion_time:
clone_context.sourceInstanceDeletionTime = (
args.source_instance_deletion_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
)
if release_track == base.ReleaseTrack.ALPHA:
# ALLOCATED IP RANGE options
if args.allocated_ip_range_name:
clone_context.allocatedIpRange = args.allocated_ip_range_name
def RunBaseCloneCommand(args, release_track):
"""Clones a Cloud SQL instance.
Args:
args: argparse.Namespace, The arguments used to invoke this command.
release_track: base.ReleaseTrack, the release track that this was run under.
Returns:
A dict object representing the operations resource describing the
clone operation if the clone was successful.
Raises:
ArgumentError: The arguments are invalid for some reason.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
source_instance_ref, destination_instance_ref = (
_GetInstanceRefsFromArgs(args, client))
request = sql_messages.SqlInstancesCloneRequest(
project=source_instance_ref.project,
instance=source_instance_ref.instance,
instancesCloneRequest=sql_messages.InstancesCloneRequest(
cloneContext=sql_messages.CloneContext(
kind='sql#cloneContext',
destinationInstanceName=destination_instance_ref.instance)))
_UpdateRequestFromArgs(request, args, sql_messages, release_track)
# Check if source has customer-managed key; show warning if so.
try:
source_instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=source_instance_ref.project,
instance=source_instance_ref.instance))
if source_instance_resource.diskEncryptionConfiguration:
command_util.ShowCmekWarning('clone', 'the source instance')
except apitools_exceptions.HttpError:
# This is for informational purposes, so don't throw an error if failure.
pass
result = sql_client.instances.Clone(request)
operation_ref = client.resource_parser.Create(
'sql.operations',
operation=result.name,
project=destination_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,
'Cloning Cloud SQL instance')
log.CreatedResource(destination_instance_ref)
rsource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=destination_instance_ref.project,
instance=destination_instance_ref.instance))
rsource.kind = None
return rsource
def AddBaseArgs(parser):
"""Add args common to all release tracks to parser."""
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddFormat(flags.GetInstanceListFormat())
parser.add_argument(
'source',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID of the source.')
parser.add_argument('destination', help='Cloud SQL instance ID of the clone.')
pitr_options_group = parser.add_group(mutex=True, required=False)
bin_log_group = pitr_options_group.add_group(
mutex=False,
required=False,
help='Binary log coordinates for point-in-time recovery.')
bin_log_group.add_argument(
'--bin-log-file-name',
required=True,
help="""\
The name of the binary log file. Enable point-in-time recovery on the
source instance to create a binary log file. If specified with
<--bin-log-position> to form a valid binary log coordinate, it defines an
earlier point in time to clone a source instance from.
For example, mysql-bin.000001.
""")
bin_log_group.add_argument(
'--bin-log-position',
type=int,
required=True,
help="""\
Represents the state of an instance at any given point in time inside a
binary log file. If specified along with <--bin-log-file-name> to form a
valid binary log coordinate, it defines an earlier point in time to clone
a source instance from.
For example, 123 (a numeric value).
""")
point_in_time_group = pitr_options_group.add_group(
mutex=False, required=False)
point_in_time_group.add_argument(
'--point-in-time',
type=arg_parsers.Datetime.Parse,
required=True,
help="""\
Represents the state of an instance at any given point in time inside
a transaction log file. For MySQL, the binary log file is used for
transaction logs. For PostgreSQL, the write-ahead log file is used for
transaction logs. For SQL Server, the log backup file is used for
such purpose. To create a transaction log, enable point-in-time recovery
on the source instance. Instance should have transaction logs accumulated
up to the point in time they want to restore up to. Uses RFC 3339 format
in UTC timezone. If specified, defines a past state of the instance to
clone. For example, '2012-11-15T16:19:00.094Z'.
""")
point_in_time_group.add_argument(
'--restore-database-name',
required=False,
help="""\
The name of the database to be restored for a point-in-time restore. If
set, the destination instance will only restore the specified database.
""")
parser.add_argument(
'--preferred-zone',
required=False,
help="""\
The preferred zone for the cloned instance. If you specify a value for
this flag, then the destination instance uses the value as the primary
zone.
""")
parser.add_argument(
'--preferred-secondary-zone',
required=False,
help="""\
The preferred secondary zone for the cloned regional instance. If you
specify a value for this flag, then the destination instance uses the
value as the secondary zone. The secondary zone can't be the same as the
primary zone.
""")
parser.add_argument(
'--source-instance-deletion-time',
type=arg_parsers.Datetime.Parse,
required=False,
help="""\
The time the source instance was deleted. This is required if cloning
from a deleted instance.
""")
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA)
class Clone(base.CreateCommand):
"""Clones a Cloud SQL instance."""
detailed_help = DETAILED_HELP
@classmethod
def Args(cls, parser):
"""Declare flag and positional arguments for the command parser."""
AddBaseArgs(parser)
parser.display_info.AddCacheUpdater(flags.InstanceCompleter)
def Run(self, args):
return RunBaseCloneCommand(args, self.ReleaseTrack())
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class CloneAlpha(base.CreateCommand):
"""Clones a Cloud SQL instance."""
detailed_help = DETAILED_APLHA_HELP
def Run(self, args):
return RunBaseCloneCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
AddAlphaArgs(parser)
parser.display_info.AddCacheUpdater(flags.InstanceCompleter)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 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.
"""Command group for managing Sqladmin instance configurations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Config(base.Group):
"""Manage Sqladmin instance configurations."""

View File

@@ -0,0 +1,38 @@
release_tracks: [ALPHA]
command_type: CONFIG_EXPORT
help_text:
brief: Export the configuration for a Sqladmin instance.
description: |
*{command}* exports the configuration for a Sqladmin instance.
Instance configurations can be exported in
Kubernetes Resource Model (krm) or Terraform HCL formats. The
default format is `krm`.
Specifying `--all` allows you to export the configurations for all
instances within the project.
Specifying `--path` allows you to export the configuration(s) to
a local directory.
examples: |
To export the configuration for an instance, run:
$ {command} my-instance
To export the configuration for an instance to a file, run:
$ {command} my-instance --path=/path/to/dir/
To export the configuration for an instance in Terraform
HCL format, run:
$ {command} my-instance --resource-format=terraform
To export the configurations for all instances within a
project, run:
$ {command} --all
arguments:
resource:
help_text: Instance to export the configuration for.
spec: !REF googlecloudsdk.command_lib.sql.resources:instance

View File

@@ -0,0 +1,532 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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.
"""Creates a new Cloud SQL instance."""
# TODO(b/362481808): remove python2-isms.
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util as common_api_util
from googlecloudsdk.api_lib.sql import exceptions as sql_exceptions
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.kms import resource_args as kms_resource_args
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.command_lib.sql import instances as command_util
from googlecloudsdk.command_lib.sql import validate as command_validate
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.resource import resource_lex
from googlecloudsdk.core.resource import resource_property
import six
# 1h, based off of the max time it usually takes to create a SQL instance.
_INSTANCE_CREATION_TIMEOUT_SECONDS = 3600
DETAILED_HELP = {
'EXAMPLES': """\
To create a MySQL 8.0 instance with ID ``prod-instance'' that has 2
CPUs, 4 GB of RAM, and is in the region ``us-central1'' (a zone will be
auto-assigned), where the 'root' user has its password set to
``password123'', run:
$ {command} prod-instance --database-version=MYSQL_8_0 --cpu=2 --memory=4GB --region=us-central1 --root-password=password123
To create a Postgres 15 instance with ID ``prod-instance'' that has 2
CPUs, 8 GiB of RAM, and is in the zone ``us-central1-a'', where the
'postgres' user has its password set to ``password123'', run:
$ {command} prod-instance --database-version=POSTGRES_15 --cpu=2 --memory=8GiB --zone=us-central1-a --root-password=password123
To create a SQL Server 2022 Express instance with ID ``prod-instance''
that has 2 CPUs, 3840MiB of RAM, and is in the zone ``us-central1-a'',
where the 'sqlserver' user has its password set to ``password123'',
run:
$ {command} prod-instance --database-version=SQLSERVER_2022_EXPRESS --cpu=2 --memory=3840MiB --zone=us-central1-a --root-password=password123
""",
}
def AddBaseArgs(parser):
"""Declare flag and positional arguments for this command parser."""
# TODO(b/35705305): move common flags to command_lib.sql.flags
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddFormat(flags.GetInstanceListFormat())
flags.AddActivationPolicy(parser)
flags.AddActiveDirectoryDomain(parser)
flags.AddAssignIp(parser)
flags.AddAuthorizedNetworks(parser)
flags.AddAvailabilityType(parser)
flags.AddBackup(parser)
flags.AddBackupStartTime(parser)
flags.AddBackupLocation(parser, allow_empty=False)
flags.AddCPU(parser)
flags.AddInstanceCollation(parser)
flags.AddDatabaseFlags(parser)
flags.AddEnableBinLog(parser, show_negated_in_help=False)
flags.AddRetainedBackupsCount(parser)
flags.AddRetainedTransactionLogDays(parser)
flags.AddFailoverReplicaName(parser)
parser.add_argument(
'instance',
type=command_validate.InstanceNameRegexpValidator(),
help='Cloud SQL instance ID.',
)
flags.AddMaintenanceReleaseChannel(parser)
flags.AddMaintenanceWindowDay(parser)
flags.AddMaintenanceWindowHour(parser)
flags.AddDenyMaintenancePeriodStartDate(parser)
flags.AddDenyMaintenancePeriodEndDate(parser)
flags.AddDenyMaintenancePeriodTime(parser)
flags.AddInsightsConfigQueryInsightsEnabled(parser, show_negated_in_help=True)
flags.AddInsightsConfigQueryStringLength(parser)
flags.AddInsightsConfigRecordApplicationTags(
parser, show_negated_in_help=True
)
flags.AddInsightsConfigRecordClientAddress(parser, show_negated_in_help=True)
flags.AddInsightsConfigQueryPlansPerMinute(parser)
flags.AddMasterInstanceName(parser)
flags.AddMemory(parser)
flags.AddPasswordPolicyMinLength(parser)
flags.AddPasswordPolicyComplexity(parser)
flags.AddPasswordPolicyReuseInterval(parser)
flags.AddPasswordPolicyDisallowUsernameSubstring(parser)
flags.AddPasswordPolicyPasswordChangeInterval(parser)
flags.AddPasswordPolicyEnablePasswordPolicy(parser)
flags.AddReplicaType(parser)
flags.AddReplication(parser)
flags.AddRequireSsl(parser)
flags.AddRootPassword(parser)
flags.AddStorageAutoIncrease(parser)
flags.AddStorageSize(parser)
flags.AddStorageType(parser)
flags.AddTier(parser)
flags.AddEdition(parser)
kms_flag_overrides = {
'kms-key': '--disk-encryption-key',
'kms-keyring': '--disk-encryption-key-keyring',
'kms-location': '--disk-encryption-key-location',
'kms-project': '--disk-encryption-key-project',
}
kms_resource_args.AddKmsKeyResourceArg(
parser, 'instance', flag_overrides=kms_flag_overrides
)
flags.AddEnablePointInTimeRecovery(parser)
flags.AddNetwork(parser)
flags.AddSqlServerAudit(parser)
flags.AddDeletionProtection(parser)
flags.AddSqlServerTimeZone(parser)
flags.AddConnectorEnforcement(parser)
flags.AddTimeout(parser, _INSTANCE_CREATION_TIMEOUT_SECONDS)
flags.AddEnableGooglePrivatePath(parser, show_negated_in_help=False)
flags.AddThreadsPerCore(parser)
flags.AddCascadableReplica(parser)
flags.AddEnableDataCache(parser)
flags.AddEnableAutoUpgrade(parser)
flags.AddRecreateReplicasOnPrimaryCrash(parser)
psc_setup_group = parser.add_group()
flags.AddEnablePrivateServiceConnect(psc_setup_group)
flags.AddAllowedPscProjects(psc_setup_group)
flags.AddCustomSubjectAlternativeNames(parser)
flags.AddSslMode(parser)
flags.AddEnableGoogleMLIntegration(parser)
flags.AddEnableDataplexIntegration(parser)
flags.AddPscAutoConnections(parser)
flags.AddServerCaMode(parser)
flags.AddTags(parser)
flags.AddRetainBackupsOnDelete(parser)
flags.AddServerCaPool(parser)
flags.AddStorageProvisionedIops(parser)
flags.AddStorageProvisionedThroughput(parser)
flags.AddInstanceType(parser)
flags.AddNodeCount(parser)
flags.AddActiveDirectoryMode(parser)
flags.AddActiveDirectorySecretManagerKey(parser)
flags.AddActiveDirectoryOrganizationalUnit(parser)
flags.AddActiveDirectoryDNSServers(parser)
flags.ClearActiveDirectoryDNSServers(parser)
flags.AddForceSqlNetworkArchitecture(parser)
flags.AddFinalBackup(parser)
flags.AddFinalbackupRetentionDays(parser)
flags.AddEnableConnectionPooling(parser)
flags.AddConnectionPoolFlags(parser)
flags.AddReadPoolAutoScaleConfig(parser)
# When adding a new field for instance creation, determine if it should also
# be included in the restore to new instance command. This command uses backup
# settings to create a new instance, allowing users to override some settings.
# If the new field should be user-overridable during restore, add it to the
# restore command.
def AddBetaArgs(parser):
"""Declare beta flag and positional arguments for this command parser."""
flags.AddExternalMasterGroup(parser)
flags.AddInstanceResizeLimit(parser)
flags.AddAllocatedIpRangeName(parser)
labels_util.AddCreateLabelsFlags(parser)
flags.AddReplicationLagMaxSecondsForRecreate(parser)
flags.AddEnableDbAlignedAtomicWrites(parser)
flags.AddEnableAcceleratedReplicaMode(parser)
flags.AddDataApiAccess(parser)
flags.AddServerCertificateRotationMode(parser)
flags.AddPerformanceCaptureConfig(parser, hidden=False)
flags.AddUncMappings(parser)
flags.AddSqlServerEntraId(parser)
def AddAlphaArgs(unused_parser):
"""Declare alpha flags for this command parser."""
pass
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
if args.IsKnownAndSpecified('enable_accelerated_replica_mode'):
if not (
master_instance_resource.databaseVersion.name.startswith('MYSQL_')
):
raise sql_exceptions.ArgumentError(
'--enable-accelerated-replica-mode is only supported for MySQL.'
)
# 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.'
)
if args.IsSpecified('cascadable_replica'):
if args.region == master_instance_resource.region:
raise exceptions.InvalidArgumentException(
'--cascadable-replica',
'`--cascadable-replica` can only be specified when creating a '
'replica that is in a different region than the primary.',
)
else:
if args.IsSpecified('cascadable_replica'):
raise exceptions.InvalidArgumentException(
'--cascadable-replica',
'`--cascadable-replica` can only be specified when '
'`--master-instance-name` is specified.',
)
if args.IsKnownAndSpecified('enable_accelerated_replica_mode'):
if args.IsSpecified('database_version'):
if not args.database_version.startswith('MYSQL_'):
raise sql_exceptions.ArgumentError(
'--enable-accelerated-replica-mode is only supported for MySQL.'
)
# --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'):
if not args.IsSpecified('master_instance_name'):
# if we are creating a replica, we allow enable_bin_log with no_backup,
# otherwise no_backup is not allowed with 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'
)
if args.IsKnownAndSpecified(
'allowed_psc_projects'
) and not args.IsKnownAndSpecified('enable_private_service_connect'):
raise sql_exceptions.ArgumentError(
'`--allowed-psc-projects` requires `--enable-private-service-connect`'
)
if args.IsKnownAndSpecified(
'psc_auto_connections'
) and not args.IsKnownAndSpecified('enable_private_service_connect'):
raise sql_exceptions.ArgumentError(
'`--psc-auto-connections` requires `--enable-private-service-connect`'
)
# TODO(b/372040396): Add the check for server_ca_mode when it is supported.
if args.IsKnownAndSpecified(
'custom_subject_alternative_names'
) and not args.IsKnownAndSpecified('server_ca_mode'):
raise sql_exceptions.ArgumentError(
'`--custom-subject-alternative-names` requires customer managed'
' server CA'
)
if args.database_flags is not None and any([
'sync_binlog' in args.database_flags,
'innodb_flush_log_at_trx_commit' in args.database_flags,
]):
log.warning(
'Changing innodb_flush_log_at_trx_commit '
'or sync_binlog may cause data loss. Check '
'https://cloud.google.com/sql/docs/mysql/flags'
' for more details.'
)
if (
args.IsKnownAndSpecified('performance_capture_config')
and args.IsSpecified('database_version')
and not args.database_version.startswith('MYSQL')
):
raise sql_exceptions.ArgumentError(
'`--performance-capture-config` is only supported for MySQL.'
)
if args.IsSpecified('edition') and args.edition == 'enterprise-plus':
# This logic is awkward because we only want to change behavior for "new"
# database types to avoid breaking changes.
can_infer_tier = (
args.tier
and command_util.DoesEnterprisePlusReplicaInferTierForDatabaseType(
args.database_version
)
)
if not (args.IsSpecified('tier') or can_infer_tier):
raise sql_exceptions.ArgumentError(
'`--edition=enterprise-plus` requires `--tier`'
)
instance_resource = (
command_util.InstancesV1Beta4.ConstructCreateInstanceFromArgs(
sql_messages,
args,
instance_ref=instance_ref,
release_track=release_track,
)
)
operation_ref = None
try:
result_operation = sql_client.instances.Insert(
sql_messages.SqlInstancesInsertRequest(
databaseInstance=instance_resource, project=instance_ref.project
)
)
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 for ' + args.database_version,
max_wait_seconds=args.timeout,
)
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
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.Command):
"""Creates a new Cloud SQL instance."""
detailed_help = DETAILED_HELP
def Run(self, args):
return RunBaseCreateCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
flags.AddLocationGroup(parser)
flags.AddDatabaseVersion(parser)
@base.ReleaseTracks(base.ReleaseTrack.BETA)
@base.DefaultUniverseOnly
class CreateBeta(base.Command):
"""Creates a new Cloud SQL instance."""
detailed_help = DETAILED_HELP
def Run(self, args):
return RunBaseCreateCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
flags.AddLocationGroup(parser)
AddBetaArgs(parser)
flags.AddDatabaseVersion(parser, restrict_choices=False)
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@base.DefaultUniverseOnly
class CreateAlpha(base.Command):
"""Creates a new Cloud SQL instance."""
detailed_help = DETAILED_HELP
def Run(self, args):
return RunBaseCreateCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
flags.AddLocationGroup(parser)
AddBetaArgs(parser)
AddAlphaArgs(parser)
flags.AddDatabaseVersion(parser, restrict_choices=False)

View File

@@ -0,0 +1,160 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Deletes a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
import six
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Delete(base.Command):
"""Deletes a Cloud SQL instance."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
flags.AddEnableFinalBackup(parser)
flags.AddFinalbackupDescription(parser)
expiration = parser.add_mutually_exclusive_group(required=False)
flags.AddFinalBackupExpiryTimeArgument(expiration)
flags.AddFinalbackupRetentionDays(expiration)
def Run(self, args):
"""Deletes a 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 delete
operation if the delete was successful.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
operation_ref = None
validate.ValidateInstanceName(args.instance)
instance_ref = client.resource_parser.Parse(
args.instance,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances',
)
try:
instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance
)
)
except exceptions.HttpError as error:
instance_resource = None
# We do not want to raise an error here to be consistent with the
# previous behavior. The Get and Delete have different IAM auth
# permissions. GET requires READ, and DELETE requires WRITE.
log.debug(
'Ignoring the error to get instance resource : %s',
six.text_type(error),
)
if (
instance_resource is not None
and instance_resource.settings.retainBackupsOnDelete
):
prompt = (
'All of the instance data will be lost except the existing backups'
' when the instance is deleted.'
)
else:
# TODO(b/361801536): Update the message to a link that points to public
# doc about how to retain the automated and ondemand backups.
# As the feature is not yet public, we do not have a link right now.
prompt = (
'All of the instance data will be lost when the instance is deleted.'
)
if not console_io.PromptContinue(prompt):
return None
expiry_time = None
if (
args.final_backup_retention_days is not None
and args.final_backup_retention_days > 0
):
retention_days = args.final_backup_retention_days
else:
retention_days = None
if args.final_backup_expiry_time is not None:
expiry_time = args.final_backup_expiry_time.strftime(
'%Y-%m-%dT%H:%M:%S.%fZ'
)
try:
result = sql_client.instances.Delete(
sql_messages.SqlInstancesDeleteRequest(
instance=instance_ref.instance,
project=instance_ref.project,
enableFinalBackup=args.enable_final_backup,
finalBackupTtlDays=retention_days,
finalBackupDescription=args.final_backup_description,
finalBackupExpiryTime=expiry_time,
)
)
operation_ref = client.resource_parser.Create(
'sql.operations', operation=result.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, 'Deleting Cloud SQL instance')
log.DeletedResource(instance_ref)
except exceptions.HttpError:
log.debug('operation : %s', six.text_type(operation_ref))
raise

View File

@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Retrieves information about a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import instances as instance_api_util
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk import core
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
import six.moves.http_client
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Get(base.DescribeCommand):
"""Displays configuration and metadata about a Cloud SQL instance.
Displays configuration and metadata about a Cloud SQL instance.
Information such as instance name, IP address, region, the CA certificate
and configuration settings will be displayed.
"""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use it to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
parser.display_info.AddFormat('{0} default'.format(
flags.INSTANCES_USERLABELS_FORMAT))
def Run(self, args):
"""Displays configuration and metadata about a Cloud SQL instance.
Information such as instance name, IP address, region, the CA certificate
and configuration settings will be displayed.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A DatabaseInstancePresentation object representing the instance resource
if fetching the instance was successful.
Raises:
HttpException: A http error response was received while executing api
request.
ResourceNotFoundError: The SQL instance was not found.
"""
client = api_util.SqlClient(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')
try:
instance = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance))
instance_presentation = instance_api_util.DatabaseInstancePresentation(instance)
serialized_instance_presentation = core.resource.resource_projector.MakeSerializable(instance_presentation)
if 'settings' in serialized_instance_presentation.keys() and 'ipConfiguration' in serialized_instance_presentation['settings'].keys():
ipv4Enabled = False
if 'ipv4Enabled' in serialized_instance_presentation['settings']['ipConfiguration'].keys():
ipv4Enabled = serialized_instance_presentation['settings']['ipConfiguration']['ipv4Enabled']
authorizedNetworks = []
if 'authorizedNetworks' in serialized_instance_presentation['settings']['ipConfiguration'].keys():
authorizedNetworks = serialized_instance_presentation['settings']['ipConfiguration']['authorizedNetworks']
if ipv4Enabled and len(authorizedNetworks) == 0:
serialized_instance_presentation['settings']['ipConfiguration']['message'] = (
'Configuring authorized network or using CloudSQL auth proxy or'
' language connectors is a prerequisite for connecting to Public'
' IP. Please refer to the documentation for more details'
' https://cloud.google.com/sql/docs/mysql/authorize-networks.'
)
return serialized_instance_presentation
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.ResourceNotFoundError(
'There was no instance found at {} or you are not authorized to '
'access it.'.format(instance_ref.RelativeName()))
raise calliope_exceptions.HttpException(error)

View File

@@ -0,0 +1,158 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 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.
"""Executes a statement on a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
from googlecloudsdk.core.util import files
DESCRIPTION = """\
Executes a statement on a Cloud SQL instance. It will use the
credentials of the specified Google Cloud account to connect to the
instance, so an IAM user with the same name must exist in the instance.
It doesn't support DQL or DML statements yet.
WARNING: The requests and responses might transit through intermediate
locations between your client and the location of the target instance.
"""
EXAMPLES = """\
To execute a statement on a Cloud SQL instance, run:
$ {command} instance-foo --sql="ALTER TABLE employees RENAME TO personnel;" --database=db1
"""
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES,
}
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
@base.DefaultUniverseOnly
class ExecuteSql(base.Command):
"""Executes a statement on a Cloud SQL instance."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.',
)
parser.add_argument(
'--sql',
required=True,
help=(
'SQL statement(s) to execute. It supports multiple statements as'
" well. When it starts with the character '@', the rest should be a"
' filepath to read the SQL statement(s) from.'
),
)
parser.add_argument(
'--row_limit',
type=int,
required=False,
help='Maximum number of rows to return. The default is unlimited.',
)
parser.add_argument(
'--partial_result_mode',
choices={
'PARTIAL_RESULT_MODE_UNSPECIFIED': (
'Unspecified mode, effectively the same as'
' `FAIL_PARTIAL_RESULT`.'
),
'FAIL_PARTIAL_RESULT': (
"Throw an error if the complete result can't be returned."
" Don't return the partial result."
),
'ALLOW_PARTIAL_RESULT': (
'Return the partial result and mark the field partial_result to'
" true if the complete result can't be returned. Don't throw"
' an error.'
),
},
required=False,
default=None,
help=(
'Controls how the API should respond when the SQL execution result'
' is incomplete due to size limit or other reasons. The default'
' mode is to throw an error instead of returning the partial'
' result.'
),
)
flags.AddDatabase(
parser,
'Database on which the statement is executed.',
)
def Run(self, args):
"""Executes a statement on a Cloud SQL instance.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the execution result.
"""
client = api_util.SqlClient(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',
)
if args.sql.startswith('@'):
sql = files.ReadFileContents(args.sql[1:])
else:
sql = args.sql
if args.partial_result_mode:
partial_result_mode = sql_messages.ExecuteSqlPayload.PartialResultModeValueValuesEnum.lookup_by_name(
args.partial_result_mode
)
else:
partial_result_mode = None
req_body = sql_messages.ExecuteSqlPayload(
sqlStatement=sql,
database=args.database,
rowLimit=args.row_limit,
partialResultMode=partial_result_mode,
autoIamAuthn=True,
)
return sql_client.instances.ExecuteSql(
sql_messages.SqlInstancesExecuteSqlRequest(
instance=instance_ref.instance,
project=instance_ref.project,
executeSqlPayload=req_body,
),
)

View File

@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Exports data from a Cloud SQL instance.
Exports data from a Cloud SQL instance to a Google Cloud Storage bucket as
a MySQL dump file.
"""
# TODO(b/67459595): Deprecate this command when `sql export` goes to GA.
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib import deprecation_utils
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
@deprecation_utils.DeprecateCommandAtVersion(
remove_version='205.0.0', remove=False, alt_command='gcloud sql export sql')
class Export(base.Command):
"""Exports data from a Cloud SQL instance.
Exports data from a Cloud SQL instance to a Google Cloud Storage
bucket as a MySQL dump file.
"""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
parser.add_argument(
'uri',
help='The path to the file in Google Cloud Storage where the export '
'will be stored. The URI is in the form gs://bucketName/fileName. '
'If the file already exists, the operation fails. If the filename '
'ends with .gz, the contents are compressed.')
flags.AddDatabaseList(parser, flags.DEFAULT_DATABASE_LIST_EXPORT_HELP_TEXT)
parser.add_argument(
'--table',
'-t',
type=arg_parsers.ArgList(min_length=1),
metavar='TABLE',
required=False,
help='Tables to export from the specified database. If you specify '
'tables, specify one and only one database. For Postgres instances, '
'only one table can be exported at a time.')
def Run(self, args):
"""Exports data from a 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 export
operation if the export was successful.
"""
client = api_util.SqlClient(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')
# TODO(b/36051079): add support for CSV exporting.
export_request = sql_messages.SqlInstancesExportRequest(
instance=instance_ref.instance,
project=instance_ref.project,
instancesExportRequest=sql_messages.InstancesExportRequest(
exportContext=sql_messages.ExportContext(
kind='sql#exportContext',
uri=args.uri,
databases=args.database or [],
fileType=sql_messages.ExportContext.FileTypeValueValuesEnum.SQL,
sqlExportOptions=(
sql_messages.ExportContext.SqlExportOptionsValue(
tables=args.table or [],)),
),),
)
result_operation = sql_client.instances.Export(export_request)
operation_ref = client.resource_parser.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, 'Exporting Cloud SQL instance')
log.status.write('Exported [{instance}] to [{bucket}].\n'.format(
instance=instance_ref, bucket=args.uri))
return None

View File

@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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.
"""Causes a high-availability Cloud SQL instance to failover to its replica."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Failover(base.Command):
"""Causes a high-availability Cloud SQL instance to failover."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
"""Calls the failover api method.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the failover
operation if the failover was successful.
"""
client = api_util.SqlClient(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')
console_io.PromptContinue(
message='Failover will be initiated. Existing connections to the '
'primary instance will break and no new connection can be established '
'during the failover.',
default=True,
cancel_on_no=True)
instance = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance))
request = sql_messages.SqlInstancesFailoverRequest(
project=instance_ref.project,
instance=instance_ref.instance,
instancesFailoverRequest=sql_messages.InstancesFailoverRequest(
failoverContext=sql_messages.FailoverContext(
kind='sql#failoverContext',
settingsVersion=instance.settings.settingsVersion)))
result_operation = sql_client.instances.Failover(request)
operation_ref = client.resource_parser.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, 'Failing over Cloud SQL instance')
return None

View File

@@ -0,0 +1,198 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Retrieves the latest recovery time for a Cloud SQL instance.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
import six.moves.http_client
DESCRIPTION = ("""\
*{command}* retrieves the latest recovery time for a Cloud SQL instance.
This is the latest time that can be used to perform point in time recovery
for the Cloud SQL instance.
""")
EXAMPLES_GA = ("""\
To retrieve the latest recovery time for an instance:
$ {command} instance-foo
To retrieve the latest recovery time for an instance that has been deleted:
$ {command} instance-foo --source-instance-deletion-time '2012-11-15T16:19:00.094Z'
""")
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES_GA,
}
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class GetLatestRecoveryTime(base.Command):
"""Displays the latest recovery time to which a Cloud SQL instance can be restored to.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use it to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
parser.add_argument(
'--source-instance-deletion-time',
type=arg_parsers.Datetime.Parse,
completer=flags.InstanceCompleter,
required=False,
help=(
'The deletion time of the source instance. This is used to identify'
' the instance if it has been deleted.'
),
)
def Run(self, args):
"""Displays the latest recovery time to which a Cloud SQL instance can be restored to.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A timestamp representing the latest recovery time to which a Cloud SQL
instance can be restored to.
Raises:
HttpException: A http error response was received while executing api
request.
ResourceNotFoundError: The SQL instance isn't found.
"""
client = api_util.SqlClient(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')
try:
request = sql_messages.SqlProjectsInstancesGetLatestRecoveryTimeRequest(
project=instance_ref.project, instance=instance_ref.instance)
if args.source_instance_deletion_time:
request.sourceInstanceDeletionTime = (
args.source_instance_deletion_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
)
return sql_client.projects_instances.GetLatestRecoveryTime(request)
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.ResourceNotFoundError(
"There's no instance found at {} or you're not authorized to "
'access it.'.format(instance_ref.RelativeName()))
raise calliope_exceptions.HttpException(error)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class GetLatestRecoveryTimeBeta(base.Command):
"""Displays the latest recovery time to which a Cloud SQL instance can be restored to.
"""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use it to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
parser.add_argument(
'--source-instance-deletion-time',
type=arg_parsers.Datetime.Parse,
completer=flags.InstanceCompleter,
required=False,
help=(
'The deletion time of the source instance. This is used to identify'
' the instance if it has been deleted.'
),
)
def Run(self, args):
"""Displays the latest recovery time to which a Cloud SQL instance can be restored to.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A timestamp representing the latest recovery time to which a Cloud SQL
instance can be restored to.
Raises:
HttpException: A http error response was received while executing api
request.
ResourceNotFoundError: The SQL instance isn't found.
"""
client = api_util.SqlClient(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')
try:
request = sql_messages.SqlProjectsInstancesGetLatestRecoveryTimeRequest(
project=instance_ref.project, instance=instance_ref.instance)
if args.source_instance_deletion_time:
request.sourceInstanceDeletionTime = (
args.source_instance_deletion_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
)
return sql_client.projects_instances.GetLatestRecoveryTime(request)
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.ResourceNotFoundError(
"There's no instance found at {} or you're not authorized to "
'access it.'.format(instance_ref.RelativeName()))
raise calliope_exceptions.HttpException(error)

View File

@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Retrieves the minimal storage size a Cloud SQL instance can be shrunk."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
import six.moves.http_client
# TODO(b/265881192): remove Hidden label once we are ready to launch.
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class GetStorageShrinkConfig(base.Command):
"""Displays the minimum storage size to which a Cloud SQL instance can be decreased.
"""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use it to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
def Run(self, args):
"""Displays the minimum storage size to which a Cloud SQL instance can be decreased.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A kind string representing the request run and the minimum storage
size to which a Cloud SQL instance can be decreased.
Raises:
HttpException: A http error response was received while executing api
request.
ResourceNotFoundError: The SQL instance isn't found.
"""
client = api_util.SqlClient(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')
try:
request = sql_messages.SqlProjectsInstancesGetDiskShrinkConfigRequest(
project=instance_ref.project, instance=instance_ref.instance)
instance = sql_client.projects_instances.GetDiskShrinkConfig(request)
return instance
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.ResourceNotFoundError(
"There's no instance found at {} or you're not authorized to "
'access it.'.format(instance_ref.RelativeName()))
raise calliope_exceptions.HttpException(error)

View File

@@ -0,0 +1,132 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Imports data into a Cloud SQL instance.
Imports data into a Cloud SQL instance from a MySQL dump file in
Google Cloud Storage.
"""
# TODO(b/67917387): Deprecate this command when `sql import` goes to GA.
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib import deprecation_utils
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
@deprecation_utils.DeprecateCommandAtVersion(
remove_version='205.0.0', remove=False, alt_command='gcloud sql import sql')
class Import(base.Command):
"""Imports data into a Cloud SQL instance from Google Cloud Storage.
Note: authorization is required. For more information on importing data
into Google Cloud SQL see
[](https://cloud.google.com/sql/docs/import-export/importing).
Cloud SQL supports importing CSV files and SQL dump files from both MySQL and
PostgreSQL. For more information on how to create these export formats, see
[](https://cloud.google.com/sql/docs/mysql/import-export/creating-sqldump-csv)
"""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
parser.add_argument(
'uri',
type=str,
help='Path to the MySQL dump file in Google Cloud Storage from which'
' the import is made. The URI is in the form gs://bucketName/fileName.'
' Compressed gzip files (.gz) are also supported.')
base.ASYNC_FLAG.AddToParser(parser)
flags.AddDatabase(parser, flags.DEFAULT_DATABASE_IMPORT_HELP_TEXT)
def Run(self, args):
"""Imports data into a Cloud SQL instance from Google Cloud Storage.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the import
operation if the import was successful.
"""
client = api_util.SqlClient(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')
console_io.PromptContinue(
message='Data from {0} will be imported to {1}.'.format(
args.uri, args.instance),
default=True,
cancel_on_no=True)
import_request = sql_messages.SqlInstancesImportRequest(
instance=instance_ref.instance,
project=instance_ref.project,
instancesImportRequest=sql_messages.InstancesImportRequest(
importContext=sql_messages.ImportContext(
kind='sql#importContext',
uri=args.uri,
database=args.database,
fileType=sql_messages.ImportContext.FileTypeValueValuesEnum.SQL,
),),
)
result_operation = sql_client.instances.Import(import_request)
operation_ref = client.resource_parser.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, 'Importing Cloud SQL instance')
log.status.write('Imported [{instance}] from [{bucket}].\n'.format(
instance=instance_ref, bucket=args.uri))
return None

View File

@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Lists instances in a given project.
Lists instances in a given project in the alphabetical order of the
instance name.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import instances
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
def _GetUriFromResource(resource):
"""Returns the URI for resource."""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
return client.resource_parser.Create(
'sql.instances', project=resource.project, instance=resource.name
).SelfLink()
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class List(base.ListCommand):
"""Lists Cloud SQL instances in a given project.
Lists Cloud SQL instances in a given project in the alphabetical
order of the instance name.
"""
@staticmethod
def Args(parser):
parser.display_info.AddFormat(flags.GetInstanceListFormat())
parser.display_info.AddUriFunc(_GetUriFromResource)
flags.AddShowEdition(parser)
flags.AddShowSqlNetworkArchitecture(parser)
flags.AddShowTransactionalLogStorageState(parser)
def Run(self, args):
"""Lists Cloud SQL instances in a given project.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
SQL instance resource iterator.
"""
if args.show_edition:
args.GetDisplayInfo().AddFormat(flags.GetInstanceListFormatEdition())
if args.show_sql_network_architecture:
args.GetDisplayInfo().AddFormat(
flags.GetInstanceListFormatForNetworkArchitectureUpgrade()
)
if args.show_transactional_log_storage_state:
args.GetDisplayInfo().AddFormat(
flags.GetInstanceListFormatForTransactionalLogStorageSwitch()
)
return instances.InstancesV1Beta4.GetDatabaseInstances(
limit=args.limit, batch_size=args.page_size
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA)
class ListBeta(base.ListCommand):
"""Lists Cloud SQL instances in a given project.
Lists Cloud SQL instances in a given project in the alphabetical
order of the instance name.
"""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
parser.display_info.AddFormat(flags.GetInstanceListFormat())
parser.display_info.AddUriFunc(_GetUriFromResource)
flags.AddShowEdition(parser)
flags.AddShowSqlNetworkArchitecture(parser)
flags.AddShowTransactionalLogStorageState(parser)
def Run(self, args):
"""Lists Cloud SQL instances in a given project.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
SQL instance resource iterator.
"""
if args.show_edition:
args.GetDisplayInfo().AddFormat(flags.GetInstanceListFormatEdition())
if args.show_sql_network_architecture:
args.GetDisplayInfo().AddFormat(
flags.GetInstanceListFormatForNetworkArchitectureUpgrade()
)
if args.show_transactional_log_storage_state:
args.GetDisplayInfo().AddFormat(
flags.GetInstanceListFormatForTransactionalLogStorageSwitch()
)
return instances.InstancesV1Beta4.GetDatabaseInstances(
limit=args.limit, batch_size=args.page_size
)

View File

@@ -0,0 +1,673 @@
# -*- coding: utf-8 -*- #
# Copyright 2016 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.
"""Updates the settings of a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import annotations
from __future__ import division
from __future__ import unicode_literals
import copy
import datetime
from typing import Optional
from apitools.base.protorpclite import messages
from apitools.base.py import encoding
from googlecloudsdk.api_lib.sql import api_util as common_api_util
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import instances as api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.command_lib.sql import instances as command_util
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
_NINE_MONTHS_IN_DAYS = 270
_TWELVE_MONTHS_IN_DAYS = 365
class _Result(object):
"""Run() method result object."""
def __init__(self, new, old):
self.new = new
self.old = old
def _PrintAndConfirmWarningMessage(args, database_version):
"""Print and confirm warning indicating the effect of applying the patch."""
continue_msg = None
insights_query_length_changed = (
'insights_config_query_string_length' in args
and args.insights_config_query_string_length is not None
)
active_directory_config_changed = any([
args.active_directory_domain is not None,
args.clear_active_directory is not None,
args.clear_active_directory_dns_servers is not None,
args.active_directory_dns_servers is not None,
args.active_directory_secret_manager_key is not None,
args.active_directory_organizational_unit is not None,
args.active_directory_mode is not None,
])
if any([
args.tier,
args.enable_database_replication is not None,
active_directory_config_changed,
insights_query_length_changed,
]):
continue_msg = ('WARNING: This patch modifies a value that requires '
'your instance to be restarted. Submitting this patch '
'will immediately restart your instance if it\'s running.')
elif any([args.database_flags, args.clear_database_flags]):
database_type_fragment = 'mysql'
if api_util.InstancesV1Beta4.IsPostgresDatabaseVersion(database_version):
database_type_fragment = 'postgres'
elif api_util.InstancesV1Beta4.IsSqlServerDatabaseVersion(database_version):
database_type_fragment = 'sqlserver'
flag_docs_url = 'https://cloud.google.com/sql/docs/{}/flags'.format(
database_type_fragment)
if args.database_flags is not None and any([
'sync_binlog' in args.database_flags,
'innodb_flush_log_at_trx_commit' in args.database_flags,
]):
log.warning(
'Changing innodb_flush_log_at_trx_commit '
'or sync_binlog may cause data loss. Check {}'
' for more details.'.format(flag_docs_url)
)
continue_msg = (
'WARNING: This patch modifies database flag values, which may require '
'your instance to be restarted. Check the list of supported flags - '
'{} - to see if your instance will be restarted when this patch '
'is submitted.'.format(flag_docs_url)
)
else:
if any([args.follow_gae_app, args.gce_zone]):
continue_msg = (
'WARNING: This patch modifies the zone your instance '
'is set to run in, which may require it to be moved. '
'Submitting this patch will restart your instance '
'if it is running in a different zone.'
)
if 'time_zone' in args and args.time_zone is not None:
time_zone_warning_msg = (
'WARNING: This patch modifies the time zone for your instance which may'
' cause inconsistencies in your data.'
)
log.warning(
'This patch modifies the time zone for your instance which may cause'
' inconsistencies in your data.'
)
if continue_msg:
continue_msg = continue_msg + '\n' + time_zone_warning_msg
else:
continue_msg = time_zone_warning_msg
if continue_msg and not console_io.PromptContinue(continue_msg):
raise exceptions.CancelledError('canceled by the user.')
def WithoutKind(message, inline=False):
"""Remove the kind field from a proto message."""
result = message if inline else copy.deepcopy(message)
for field in result.all_fields():
if field.name == 'kind':
result.kind = None
elif isinstance(field, messages.MessageField):
value = getattr(result, field.name)
if value is not None:
if isinstance(value, list):
setattr(result, field.name,
[WithoutKind(item, True) for item in value])
else:
setattr(result, field.name, WithoutKind(value, True))
return result
def _GetConfirmedClearedFields(args, patch_instance, original_instance):
"""Clear fields according to args and confirm with user."""
cleared_fields = []
if args.clear_gae_apps:
cleared_fields.append('settings.authorizedGaeApplications')
if args.clear_authorized_networks:
cleared_fields.append('settings.ipConfiguration.authorizedNetworks')
if args.clear_database_flags:
cleared_fields.append('settings.databaseFlags')
if args.remove_deny_maintenance_period:
cleared_fields.append('settings.denyMaintenancePeriods')
if args.clear_password_policy:
cleared_fields.append('settings.passwordValidationPolicy')
if args.IsKnownAndSpecified('clear_allowed_psc_projects'):
cleared_fields.append(
'settings.ipConfiguration.pscConfig.allowedConsumerProjects'
)
if args.IsKnownAndSpecified('clear_psc_auto_connections'):
cleared_fields.append(
'settings.ipConfiguration.pscConfig.pscAutoConnections'
)
if args.IsKnownAndSpecified('clear_custom_subject_alternative_names'):
cleared_fields.append(
'settings.ipConfiguration.customSubjectAlternativeNames'
)
if args.IsKnownAndSpecified('clear_connection_pool_flags'):
cleared_fields.append('settings.connectionPoolConfig.flags')
if args.IsKnownAndSpecified('clear_psc_network_attachment_uri'):
cleared_fields.append(
'settings.ipConfiguration.pscConfig.networkAttachmentUri'
)
if args.IsKnownAndSpecified('clear_unc_mappings'):
cleared_fields.append('settings.uncMappings')
if args.clear_active_directory_dns_servers:
cleared_fields.append('settings.activeDirectoryConfig.dnsServers')
if args.clear_active_directory:
cleared_fields.append('settings.activeDirectoryConfig')
log.status.write(
'The following message will be used for the patch API method.\n'
)
log.status.write(
encoding.MessageToJson(
WithoutKind(patch_instance), include_fields=cleared_fields
)
+ '\n'
)
_PrintAndConfirmWarningMessage(args, original_instance.databaseVersion)
return cleared_fields
def AddBaseArgs(parser):
"""Adds base args and flags to the parser."""
# TODO(b/35705305): move common flags to command_lib.sql.flags
flags.AddActivationPolicy(parser)
flags.AddActiveDirectoryDomain(parser)
flags.AddAssignIp(parser)
base.ASYNC_FLAG.AddToParser(parser)
gae_apps_group = parser.add_mutually_exclusive_group()
flags.AddAuthorizedGAEApps(gae_apps_group, update=True)
gae_apps_group.add_argument(
'--clear-gae-apps',
required=False,
action='store_true',
help=('Specified to clear the list of App Engine apps that can access '
'this instance.'))
networks_group = parser.add_mutually_exclusive_group()
flags.AddAuthorizedNetworks(networks_group, update=True)
networks_group.add_argument(
'--clear-authorized-networks',
required=False,
action='store_true',
help=('Clear the list of external networks that are allowed to connect '
'to the instance.'))
flags.AddAvailabilityType(parser)
backups_group = parser.add_mutually_exclusive_group()
backups_enabled_group = backups_group.add_group()
flags.AddBackupStartTime(backups_enabled_group)
flags.AddBackupLocation(backups_enabled_group, allow_empty=True)
flags.AddRetainedBackupsCount(backups_enabled_group)
flags.AddRetainedTransactionLogDays(backups_enabled_group)
backups_group.add_argument(
'--no-backup',
required=False,
action='store_true',
help='Specified if daily backup should be disabled.')
database_flags_group = parser.add_mutually_exclusive_group()
flags.AddDatabaseFlags(database_flags_group)
database_flags_group.add_argument(
'--clear-database-flags',
required=False,
action='store_true',
help=('Clear the database flags set on the instance. '
'WARNING: Instance will be restarted.'))
flags.AddCPU(parser)
parser.add_argument(
'--diff',
action='store_true',
help='Show what changed as a result of the update.')
flags.AddEnableBinLog(parser, show_negated_in_help=True)
parser.add_argument(
'--enable-database-replication',
action=arg_parsers.StoreTrueFalseAction,
help=('Enable database replication. Applicable only for read replica '
'instance(s). WARNING: Instance will be restarted.'))
parser.add_argument(
'--follow-gae-app',
required=False,
help=('First Generation instances only. The App Engine app '
'this instance should follow. It must be in the same region as '
'the instance. WARNING: Instance may be restarted.'))
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
flags.AddMaintenanceReleaseChannel(parser)
parser.add_argument(
'--maintenance-window-any',
action='store_true',
help='Removes the user-specified maintenance window.')
flags.AddMaintenanceWindowDay(parser)
flags.AddMaintenanceWindowHour(parser)
flags.AddDenyMaintenancePeriodStartDate(parser)
flags.AddDenyMaintenancePeriodEndDate(parser)
flags.AddDenyMaintenancePeriodTime(parser)
parser.add_argument(
'--remove-deny-maintenance-period',
action='store_true',
help='Removes the user-specified deny maintenance period.')
flags.AddInsightsConfigQueryInsightsEnabled(parser, show_negated_in_help=True)
flags.AddInsightsConfigQueryStringLength(parser)
flags.AddInsightsConfigRecordApplicationTags(
parser, show_negated_in_help=True)
flags.AddInsightsConfigRecordClientAddress(parser, show_negated_in_help=True)
flags.AddInsightsConfigQueryPlansPerMinute(parser)
flags.AddMemory(parser)
flags.AddPasswordPolicyMinLength(parser)
flags.AddPasswordPolicyComplexity(parser)
flags.AddPasswordPolicyReuseInterval(parser)
flags.AddPasswordPolicyDisallowUsernameSubstring(parser)
flags.AddPasswordPolicyPasswordChangeInterval(parser)
flags.AddPasswordPolicyEnablePasswordPolicy(parser)
flags.AddPasswordPolicyClearPasswordPolicy(parser)
parser.add_argument(
'--pricing-plan',
'-p',
required=False,
choices=['PER_USE', 'PACKAGE'],
help=('First Generation instances only. The pricing plan for this '
'instance.'))
flags.AddReplication(parser)
parser.add_argument(
'--require-ssl',
action=arg_parsers.StoreTrueFalseAction,
help=('mysqld should default to \'REQUIRE X509\' for users connecting '
'over IP.'))
flags.AddStorageAutoIncrease(parser)
flags.AddStorageSize(parser)
flags.AddStorageType(parser)
flags.AddTier(parser, is_patch=True)
flags.AddEdition(parser)
flags.AddEnablePointInTimeRecovery(parser)
flags.AddNetwork(parser)
flags.AddMaintenanceVersion(parser)
flags.AddSqlServerAudit(parser)
flags.AddSqlServerTimeZone(parser)
flags.AddDeletionProtection(parser)
flags.AddConnectorEnforcement(parser)
flags.AddEnableGooglePrivatePath(parser, show_negated_in_help=True)
flags.AddThreadsPerCore(parser)
flags.AddEnableDataCache(parser)
flags.AddEnableAutoUpgrade(parser)
flags.AddRecreateReplicasOnPrimaryCrash(parser)
psc_update_group = parser.add_mutually_exclusive_group()
flags.AddAllowedPscProjects(psc_update_group)
flags.AddClearAllowedPscProjects(psc_update_group)
ip_update_custom_sans_group = parser.add_mutually_exclusive_group()
flags.AddCustomSubjectAlternativeNames(ip_update_custom_sans_group)
flags.AddClearCustomSubjectAlternativeNames(ip_update_custom_sans_group)
flags.AddSslMode(parser)
flags.AddEnableGoogleMLIntegration(parser)
flags.AddEnableDataplexIntegration(parser)
flags.AddUpgradeSqlNetworkArchitecture(parser)
flags.AddForceSqlNetworkArchitecture(parser)
flags.AddSimulateMaintenanceEvent(parser)
flags.AddSwitchTransactionLogsToCloudStorage(parser)
flags.AddFailoverDrReplicaName(parser)
flags.AddClearFailoverDrReplicaName(parser)
flags.AddIncludeReplicasForMajorVersionUpgrade(parser)
flags.AddRetainBackupsOnDelete(parser)
flags.AddStorageProvisionedIops(parser)
flags.AddStorageProvisionedThroughput(parser)
flags.AddEnablePrivateServiceConnect(parser, show_negated_in_help=True)
psc_na_uri_update_group = parser.add_mutually_exclusive_group()
flags.AddPSCNetworkAttachmentUri(psc_na_uri_update_group)
flags.AddClearPSCNetworkAttachmentUri(psc_na_uri_update_group)
flags.AddInstanceType(parser)
flags.AddNodeCount(parser)
flags.AddActiveDirectoryMode(parser)
flags.AddActiveDirectorySecretManagerKey(parser)
flags.AddActiveDirectoryOrganizationalUnit(parser)
flags.AddActiveDirectoryDNSServers(parser)
flags.ClearActiveDirectoryDNSServers(parser)
flags.AddClearActiveDirectory(parser)
flags.AddFinalBackup(parser)
flags.AddFinalbackupRetentionDays(parser)
flags.AddEnableConnectionPooling(parser)
connection_pool_flags_group = parser.add_mutually_exclusive_group()
flags.AddConnectionPoolFlags(connection_pool_flags_group)
flags.AddClearConnectionPoolFlags(connection_pool_flags_group)
psc_update_auto_connections_group = parser.add_mutually_exclusive_group()
flags.AddPscAutoConnections(psc_update_auto_connections_group)
flags.AddClearPscAutoConnections(psc_update_auto_connections_group)
flags.AddServerCaMode(parser)
flags.AddServerCaPool(parser)
flags.AddReadPoolAutoScaleConfig(parser)
def AddBetaArgs(parser):
"""Adds beta args and flags to the parser."""
flags.AddInstanceResizeLimit(parser)
flags.AddAllocatedIpRangeName(parser)
labels_util.AddUpdateLabelsFlags(parser, enable_clear=True)
flags.AddReplicationLagMaxSecondsForRecreate(parser)
flags.AddReconcilePsaNetworking(parser)
flags.AddEnableAcceleratedReplicaMode(parser)
unc_mappings_group = parser.add_mutually_exclusive_group(hidden=True)
flags.AddUncMappings(unc_mappings_group)
flags.AddClearUncMappings(unc_mappings_group)
flags.AddDataApiAccess(parser)
flags.AddServerCertificateRotationMode(parser)
flags.AddPerformanceCaptureConfig(parser, hidden=False)
flags.AddSqlServerEntraId(parser)
flags.AddClearEntraIdConfig(parser)
def AddAlphaArgs(unused_parser):
"""Adds alpha args and flags to the parser."""
pass
def IsBetaOrNewer(release_track):
"""Returns true if the release track is beta or newer."""
return (
release_track == base.ReleaseTrack.BETA
or release_track == base.ReleaseTrack.ALPHA
)
def RunBasePatchCommand(args, release_track):
"""Updates settings of a Cloud SQL instance using the patch api method.
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 patch
operation if the patch was successful.
Raises:
CancelledError: The user chose not to continue.
"""
if args.diff and not args.IsSpecified('format'):
args.format = 'diff(old, new)'
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')
# If the flag to simulate a maintenance event is supplied along with other
# flags thrown an error.
if args.IsSpecified(
'simulate_maintenance_event'
):
for key in args.GetSpecifiedArgsDict():
# positional argument does not have a flag argument
if key == 'instance':
continue
if key == 'simulate_maintenance_event':
continue
if not args.GetFlagArgument(key).is_global:
raise exceptions.ArgumentError(
'`--simulate-maintenance-event` cannot be specified with other'
' arguments excluding gCloud wide flags'
)
if args.IsSpecified('no_backup'):
if args.IsSpecified('enable_bin_log'):
raise exceptions.ArgumentError(
'`--enable-bin-log` cannot be specified when --no-backup is '
'specified')
elif args.IsSpecified('enable_point_in_time_recovery'):
raise exceptions.ArgumentError(
'`--enable-point-in-time-recovery` cannot be specified when '
'--no-backup is specified')
if args.IsKnownAndSpecified('failover_dr_replica_name'):
if args.IsKnownAndSpecified('clear_failover_dr_replica_name'):
raise exceptions.ArgumentError(
'`--failover-dr-replica-name` cannot be specified when '
'--clear-failover-dr-replica-name is specified')
# If --authorized-networks is used, confirm that the user knows the networks
# will get overwritten.
if args.authorized_networks:
api_util.InstancesV1Beta4.PrintAndConfirmAuthorizedNetworksOverwrite()
original_instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance))
if args.IsKnownAndSpecified(
'performance_capture_config'
) and not original_instance_resource.databaseVersion.name.startswith('MYSQL'):
raise exceptions.ArgumentError(
'`--performance-capture-config` is only supported for MySQL instances.'
)
if (
args.IsSpecified('deny_maintenance_period_start_date')
or args.IsSpecified('deny_maintenance_period_end_date')
or args.IsSpecified('deny_maintenance_period_time')
):
maintenance_version = original_instance_resource.maintenanceVersion
if maintenance_version:
maintenance_date = _ParseDateFromMaintenanceVersion(maintenance_version)
if maintenance_date:
today = datetime.date.today()
delta = today - maintenance_date
# 9 months ~ 270 days, 12 months ~ 365 days.
if _NINE_MONTHS_IN_DAYS <= delta.days < _TWELVE_MONTHS_IN_DAYS:
log.warning(
'Your instance has NOT undergone maintenance for at least 9'
' months. It is highly recommended to perform it soon. While you'
' can still set a deny maintenance period now, please be aware'
' that once your instance is on a maintenance version that is at'
' least 12 months old, you will no longer be able to set a deny'
' period. Maintenance is crucial for important updates, security'
' patches, and bug fixes, and skipping them can leave your'
' instance vulnerable. You can learn more about how to perform'
' maintenance here:'
' https://cloud.google.com/sql/docs/mysql/maintenance'
)
if IsBetaOrNewer(release_track) and args.IsSpecified(
'reconcile_psa_networking'
):
if (
not original_instance_resource.settings.ipConfiguration
or not original_instance_resource.settings.ipConfiguration.privateNetwork
):
raise exceptions.ArgumentError(
'argument --reconcile-psa-networking can be used only with instances'
' that have a private network'
)
# Do not allow reconcile-psa-networking flag to be specified with other
# arguments.
for key in args.GetSpecifiedArgsDict():
# positional argument does not have a flag argument
if key == 'instance':
continue
if key == 'reconcile_psa_networking':
continue
if not args.GetFlagArgument(key).is_global:
raise exceptions.ArgumentError(
'argument --reconcile-psa-networking cannot be specified with other'
' arguments excluding gcloud wide flags'
)
if args.IsKnownAndSpecified('enable_accelerated_replica_mode'):
if not api_util.InstancesV1Beta4.IsMysqlDatabaseVersion(
original_instance_resource.databaseVersion
):
raise exceptions.ArgumentError(
'--enable-accelerated-replica-mode is only supported for MySQL.'
)
patch_instance = command_util.InstancesV1Beta4.ConstructPatchInstanceFromArgs(
sql_messages,
args,
original=original_instance_resource,
release_track=release_track)
patch_instance.project = instance_ref.project
patch_instance.name = instance_ref.instance
cleared_fields = _GetConfirmedClearedFields(args, patch_instance,
original_instance_resource)
# beta only
if args.maintenance_window_any:
cleared_fields.append('settings.maintenanceWindow')
if args.IsKnownAndSpecified('clear_failover_dr_replica_name'):
cleared_fields.append('replicationCluster')
with sql_client.IncludeFields(cleared_fields):
result_operation = sql_client.instances.Patch(
sql_messages.SqlInstancesPatchRequest(
databaseInstance=patch_instance,
project=instance_ref.project,
instance=instance_ref.instance))
operation_ref = client.resource_parser.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,
'Patching Cloud SQL instance')
log.UpdatedResource(instance_ref)
changed_instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance))
return _Result(changed_instance_resource, original_instance_resource)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Patch(base.UpdateCommand):
"""Updates the settings of a Cloud SQL instance."""
def Run(self, args):
return RunBasePatchCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
flags.AddZone(
parser,
help_text=('Preferred Compute Engine zone (e.g. us-central1-a, '
'us-central1-b, etc.). WARNING: Instance may be restarted.'))
flags.AddDatabaseVersion(parser, support_default_version=False)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class PatchBeta(base.UpdateCommand):
"""Updates the settings of a Cloud SQL instance."""
def Run(self, args):
return RunBasePatchCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
flags.AddZone(
parser,
help_text=('Preferred Compute Engine zone (e.g. us-central1-a, '
'us-central1-b, etc.). WARNING: Instance may be restarted.'))
AddBetaArgs(parser)
flags.AddDatabaseVersion(
parser,
restrict_choices=False,
support_default_version=False)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class PatchAlpha(base.UpdateCommand):
"""Updates the settings of a Cloud SQL instance."""
def Run(self, args):
return RunBasePatchCommand(args, self.ReleaseTrack())
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command."""
AddBaseArgs(parser)
flags.AddZone(
parser,
help_text=('Preferred Compute Engine zone (e.g. us-central1-a, '
'us-central1-b, etc.). WARNING: Instance may be restarted.'))
AddBetaArgs(parser)
AddAlphaArgs(parser)
flags.AddDatabaseVersion(
parser,
restrict_choices=False,
support_default_version=False)
def _ParseDateFromMaintenanceVersion(
maintenance_version: str,
) -> Optional[datetime.date]:
"""Parses the date from a maintenance version string.
Args:
maintenance_version: The maintenance version string in a format like
'MYSQL_5_7_44.R20240915.01_02'.
Returns:
A datetime.date object if a valid date is found, otherwise None.
"""
for part in maintenance_version.replace('_', '.').split('.'):
if part.startswith('R'):
maybe_date_str = part[1:]
if len(maybe_date_str) == 8 and maybe_date_str.isdigit():
try:
return datetime.datetime.strptime(maybe_date_str, '%Y%m%d').date()
except ValueError:
# Continue searching for a valid date part.
pass
return None

View File

@@ -0,0 +1,151 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Performs a storage size decrease of a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import constants
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
import six.moves.http_client
# TODO(b/265881192): remove Hidden label once we are ready to launch.
@base.DefaultUniverseOnly
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class PerformStorageShrink(base.Command):
"""Performs a storage size decrease of a Cloud SQL instance."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
# Perform storage shrink runs for a long time, it is better to default to
# async mode to prevent the gcloud client from timing out.
base.ASYNC_FLAG.SetDefault(parser, True)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
flags.AddStorageSizeForStorageShrink(parser)
def Run(self, args):
"""Performs a storage size decrease of a 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 perform
storage shrink operation if the decrease was successful.
Raises:
HttpException: A http error response was received while executing api
request.
ResourceNotFoundError: The SQL instance wasn't found.
RequiredArgumentException: A required argument was not supplied by the
user, such as omitting --root-password on a SQL Server instance.
"""
client = api_util.SqlClient(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',
)
if not console_io.PromptContinue(
'Confirm that you have already run `gcloud alpha sql instances'
' get-storage-shrink-config $instance_name` and understand that this'
' operation will restart your database and might take a long time to'
' complete (y/n)'
):
return None
try:
if args.storage_size:
request = sql_messages.SqlProjectsInstancesPerformDiskShrinkRequest(
instance=instance_ref.instance,
project=instance_ref.project,
performDiskShrinkContext=sql_messages.PerformDiskShrinkContext(
targetSizeGb=int(args.storage_size / constants.BYTES_TO_GB),
),
)
else:
request = sql_messages.SqlProjectsInstancesPerformDiskShrinkRequest(
instance=instance_ref.instance,
project=instance_ref.project,
)
result_operation = sql_client.projects_instances.PerformDiskShrink(
request
)
operation_ref = client.resource_parser.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,
'Performing a storage size decrease on a Cloud SQL instance.',
2147483647, # 2^31-1
)
changed_instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance))
return {
'Name': instance_ref.instance,
'Project': instance_ref.project,
'StorageSizeGb': changed_instance_resource.settings.dataDiskSizeGb
}
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.ResourceNotFoundError(
"There's no instance found at {} or you're not authorized to "
'access it.'.format(instance_ref.RelativeName()))
raise calliope_exceptions.HttpException(error)

View File

@@ -0,0 +1,272 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 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.
"""Performs a point in time restore for a Cloud SQL instance managed by Google Cloud Backup and Disaster Recovery (DR) Service."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.sql import constants
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.command_lib.sql import instances as command_util
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from googlecloudsdk.generated_clients.apis.sqladmin.v1beta4 import sqladmin_v1beta4_messages
DESCRIPTION = """\
*{command}* performs a point in time restore for a Cloud SQL instance
managed by Google Cloud Backup and Disaster Recovery (DR) Service.
"""
EXAMPLES_GA = """\
To perform a point in time restore from an earlier point in time:
$ {command} datasource target-instance '2012-11-15T16:19:00.094Z'
"""
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES_GA,
}
def _GetInstanceRefFromArgs(
args: parser_extensions.Namespace, client: api_util.SqlClient
) -> resources.Resource:
"""Get validated ref to destination instance from args."""
validate.ValidateInstanceName(args.target)
return client.resource_parser.Parse(
args.target,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances',
)
def _UpdateRequestFromArgs(
request: sqladmin_v1beta4_messages.SqlInstancesPointInTimeRestoreRequest,
args: parser_extensions.Namespace,
) -> None:
"""Update request with clone options."""
pitr_context = request.pointInTimeRestoreContext
if args.restore_database_names:
pitr_context.databaseNames[:] = [args.restore_database_names]
if args.private_network:
pitr_context.privateNetwork = args.private_network
if args.preferred_zone:
pitr_context.preferredZone = args.preferred_zone
if args.preferred_secondary_zone:
pitr_context.preferredSecondaryZone = args.preferred_secondary_zone
if args.allocated_ip_range_name:
pitr_context.allocatedIpRange = args.allocated_ip_range_name
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA
)
class PointInTimeRestore(base.Command):
"""Performs a point in time restore for a Cloud SQL instance managed by Google Cloud Backup and Disaster Recovery (DR) Service."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser: parser_extensions.Namespace) -> None:
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use it to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddFormat(flags.GetInstanceListFormat())
parser.add_argument(
'datasource',
help="""\
The Google Cloud Backup and Disaster Recovery (DR) Service Datasource URI,
of the form projects/{project}/locations/{region}/backupVaults/
{backupvault}/dataSources/{datasource}.
""",
)
parser.add_argument(
'target',
help='Cloud SQL instance ID of the target instance.',
)
parser.add_argument(
'point_in_time',
type=arg_parsers.Datetime.Parse,
help="""\
The point in time in which to restore the instance to. Uses RFC 3339
format in UTC timezone. For example, '2012-11-15T16:19:00.094Z'.
""",
)
parser.add_argument(
'--private-network',
required=False,
help="""\
The resource link for the VPC network from which the Cloud SQL instance is
accessible for private IP. For example,
\'/projects/myProject/global/networks/default\'.
""",
)
parser.add_argument(
'--allocated-ip-range-name',
required=False,
help="""\
The name of the IP range allocated for the target instance with
private network connectivity. For example:
\'google-managed-services-default\'. If set, the target instance
IP is created in the allocated range represented by this name.
Reserved for future use.
""",
)
parser.add_argument(
'--preferred-zone',
required=False,
help="""\
The preferred zone for the target instance. If you specify a value for
this flag, then the target instance uses the value as the primary
zone.
""",
)
parser.add_argument(
'--preferred-secondary-zone',
required=False,
help="""\
The preferred secondary zone for the cloned regional instance. If you
specify a value for this flag, then the target instance uses the value
as the secondary zone. The secondary zone can't be the same as the
primary zone.
""",
)
parser.add_argument(
'--restore-database-names',
required=False,
help="""\
The name of the databases to be restored for a point-in-time restore. If
set, the destination instance will only restore the specified databases.
""",
)
flags.AddSourceInstanceOverrideArgs(parser=parser, for_pitr=True)
def Run(self, args: parser_extensions.Namespace):
"""Performs a point in time restore for a Cloud SQL instance.
The instance is managed by Google Cloud Backup and Disaster Recovery
(DR) Service.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing if the point-in-time restore operation was
successful.
Raises:
ArgumentError: The arguments are invalid.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
specified_args_dict = getattr(args, '_specified_args', None)
overrides = [
key
for key in specified_args_dict
if key in constants.TARGET_INSTANCE_OVERRIDE_FLAGS
]
clears = [
key
for key in specified_args_dict
if key in constants.TARGET_INSTANCE_CLEAR_FLAGS
]
request = sql_messages.SqlInstancesPointInTimeRestoreRequest(
parent=f'projects/{properties.VALUES.core.project.GetOrFail()}',
pointInTimeRestoreContext=sql_messages.PointInTimeRestoreContext(
datasource=args.datasource,
targetInstance=args.target,
pointInTime=args.point_in_time.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
),
)
_UpdateRequestFromArgs(request, args)
destination_instance_ref = _GetInstanceRefFromArgs(args, client)
# If the request has overrides, construct the target instance resource from
# the args and pass it with the request. Note that currently, overrides are
# only supported for cross project PITR. But here in gcloud, we do not have
# a way to know whether the PITR is cross project because we do not know the
# source project. So the check is done in the backend, where if the source
# and target projects are not different, the overrides will be ignored.
if overrides:
instance_resource = (
command_util.InstancesV1Beta4.ConstructCreateInstanceFromArgs(
sql_messages, args, instance_ref=destination_instance_ref
)
)
request.pointInTimeRestoreContext.targetInstanceSettings = (
instance_resource
)
if clears:
request.pointInTimeRestoreContext.targetInstanceClearSettingsFieldNames = (
flags.GetInstanceClearOverrides(args)
)
response = sql_client.instances.PointInTimeRestore(request)
operation_ref = client.resource_parser.Create(
'sql.operations',
operation=response.name,
project=destination_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, 'Performing point-in-time restore'
)
log.CreatedResource(destination_instance_ref)
resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=destination_instance_ref.project,
instance=destination_instance_ref.instance,
)
)
resource.kind = None
return resource

View File

@@ -0,0 +1,184 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 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.
"""Performs pre-checks for a major version upgrade of a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import time
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
import six.moves.http_client
DESCRIPTION = """
*{command}* performs pre-checks for a major version upgrade of a Cloud SQL instance.
"""
EXAMPLES = """
To perform pre-checks before upgrading to a target version:
$ {command} test-instance --target-database-version=POSTGRES_15
"""
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES,
}
@base.ReleaseTracks(base.ReleaseTrack.GA)
@base.UniverseCompatible
class PreCheckMajorVersionUpgrade(base.Command):
"""Performs pre-checks for a major version upgrade of a Cloud SQL instance."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.',
)
parser.add_argument(
'--target-database-version',
required=True,
help='Target database version for the upgrade.',
)
def Run(self, args):
client = api_util.SqlClient(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',
)
try:
try:
target_version_enum = sql_messages.PreCheckMajorVersionUpgradeContext.TargetDatabaseVersionValueValuesEnum(
args.target_database_version.upper()
)
except TypeError:
raise exceptions.InvalidArgumentException(
'target-database-version',
'Missing or Invalid parameter: Target database version.',
)
# Perform the pre-check
request = sql_messages.SqlInstancesPreCheckMajorVersionUpgradeRequest(
instance=args.instance,
project=instance_ref.project,
instancesPreCheckMajorVersionUpgradeRequest=
sql_messages.InstancesPreCheckMajorVersionUpgradeRequest(
preCheckMajorVersionUpgradeContext=sql_messages.PreCheckMajorVersionUpgradeContext(
targetDatabaseVersion=target_version_enum
)
)
)
result_operation = (
sql_client.instances.PreCheckMajorVersionUpgrade(request)
)
operation_ref = client.resource_parser.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
)
)
# Custom polling logic instead of WaitForOperation
while True:
op = sql_client.operations.Get(
sql_messages.SqlOperationsGetRequest(
project=operation_ref.project,
operation=operation_ref.operation
)
)
if op.status == sql_messages.Operation.StatusValueValuesEnum.DONE:
break
time.sleep(1)
context = getattr(op, 'preCheckMajorVersionUpgradeContext', None)
precheck_responses = (
getattr(context, 'preCheckResponse', []) if context else []
)
formatted_responses = []
for resp in precheck_responses:
raw_message = resp.message
# Remove unwanted suffix if present
if raw_message.endswith('"]'):
raw_message = raw_message[:-2]
clean_message = raw_message.strip().strip('"')
if 'pre-upgrade check failed' not in clean_message:
formatted_responses.append({
'message': clean_message,
'message_type': resp.messageType.name,
'actions_required': resp.actionsRequired,
})
print('Performing pre-check on Cloud SQL instance....done')
if precheck_responses:
print(
'Please check the'
' output in the PreCheckResults field for more details.'
)
else:
print(
'No issues or warnings detected during pre-check. We recommend that'
' you perform MVU on a cloned instance before applying them to a'
' production instance.'
)
return {
'Name': instance_ref.instance,
'Project': instance_ref.project,
'TargetDatabaseVersion': (
str(context.targetDatabaseVersion)
if context
else args.target_database_version.upper()
),
'PreCheckResults': formatted_responses,
'Status': 'COMPLETED',
}
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.HttpException(
"There's no instance found at {} or you're not authorized to"
' access it.'.format(instance_ref.RelativeName())
)
raise exceptions.HttpException(error)

View File

@@ -0,0 +1,133 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Promotes Cloud SQL read replica to a stand-alone instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import sys
import textwrap
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import instances
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class PromoteReplica(base.Command):
"""Promotes Cloud SQL read replica to a stand-alone instance."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'replica',
completer=flags.InstanceCompleter,
help='Cloud SQL read replica ID.')
flags.AddFailoverFlag(parser)
def Run(self, args):
"""Promotes Cloud SQL read replica to a stand-alone instance.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the
promote-replica operation if the promote-replica was successful.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
validate.ValidateInstanceName(args.replica)
instance_ref = client.resource_parser.Parse(
args.replica,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances')
instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance))
if instances.InstancesV1Beta4.IsMysqlDatabaseVersion(
instance_resource.databaseVersion):
database_type_fragment = 'mysql'
elif instances.InstancesV1Beta4.IsPostgresDatabaseVersion(
instance_resource.databaseVersion):
database_type_fragment = 'postgres'
else:
# TODO(b/144067325): currently the link below goes to extremely
# database-specific instructions to query the replication lag, so in case
# we (...somehow...) end up here for a db other than mysql or postgres,
# it's probably better to show nothing than to link to misleading info.
# Once the metrics are made uniform in b/144067325, then we could default
# to something here as we'd be giving the same instructions for all dbs
# anyway.
database_type_fragment = None
promote_replica_docs_link = None
if database_type_fragment:
promote_replica_docs_link = (
'Learn more:\n' +
'https://cloud.google.com/sql/docs/{}/replication/manage-replicas#promote-replica\n\n'
.format(database_type_fragment))
# Format the message ourselves here rather than supplying it as part of the
# 'message' to PromptContinue. Having the whole paragraph be automatically
# formatted by PromptContinue would leave it with a line break in the middle
# of the URL, rendering it unclickable.
sys.stderr.write(textwrap.TextWrapper().fill(
'Promoting a read replica stops replication and converts the instance '
'to a standalone primary instance with read and write capabilities. '
'This can\'t be undone. To avoid loss of data, before promoting the '
'replica, you should verify that the replica has applied all '
'transactions received from the primary.') + '\n\n')
if promote_replica_docs_link:
sys.stderr.write(promote_replica_docs_link)
console_io.PromptContinue(message='', default=True, cancel_on_no=True)
result = sql_client.instances.PromoteReplica(
sql_messages.SqlInstancesPromoteReplicaRequest(
project=instance_ref.project, instance=instance_ref.instance,
failover=args.failover))
operation_ref = client.resource_parser.Create(
'sql.operations', operation=result.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,
'Promoting Cloud SQL replica')
log.status.write('Promoted [{instance}].\n'.format(instance=instance_ref))

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Reencrypts a Cloud SQL CMEK instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
import six
DESCRIPTION = """\
Reencrypt a Cloud SQL CMEK instance with the primary key version.
"""
EXAMPLES_GA = """\
To reencrypt a Cloud SQL CMEK instance with the primary key version:
$ {command} instance-foo
"""
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES_GA,
}
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Reencrypt(base.Command):
"""Reencrypts a Cloud SQL CMEK instance."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.',
)
def Run(self, args):
"""Reencrypts a Cloud SQL CMEK instance.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the
reencrypt operation if the reencryption was successful.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
operation_ref = None
validate.ValidateInstanceName(args.instance)
instance_ref = client.resource_parser.Parse(
args.instance,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances',
)
if not console_io.PromptContinue(
'WARNING: Reencryption will restart your instance if the primary key'
" version is different from the instance's key version."
):
return None
try:
result = sql_client.instances.Reencrypt(
sql_messages.SqlInstancesReencryptRequest(
instance=instance_ref.instance, project=instance_ref.project
)
)
operation_ref = client.resource_parser.Create(
'sql.operations', operation=result.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, 'Reencrypting Cloud SQL instance'
)
except exceptions.HttpError:
log.debug('operation : %s', six.text_type(operation_ref))
raise

View File

@@ -0,0 +1,106 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Releases a SQL Server Reporting Services lease on a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
DESCRIPTION = """\
Release a SQL Server Reporting Services lease on a Cloud SQL instance.
"""
EXAMPLES = """\
To release a SQL Server Reporting Services lease on an instance:
$ {command} instance-foo
"""
DETAILED_HELP = {
'DESCRIPTION': DESCRIPTION,
'EXAMPLES': EXAMPLES,
}
@base.ReleaseTracks(base.ReleaseTrack.GA)
class ReleaseSsrsLease(base.Command):
"""Releases a SQL Server Reporting Services lease on a Cloud SQL instance."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
Returns:
A dict object representing the operations resource describing the release
SSRS lease operation if the operation was successful.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.',
)
def Run(self, args):
"""Releases a SQL Server Reporting Services lease on a 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
release-ssrs-lease operation if the release-ssrs-lease was successful.
"""
client = api_util.SqlClient(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')
request = sql_messages.SqlInstancesReleaseSsrsLeaseRequest(
instance=instance_ref.instance,
project=instance_ref.project,
)
result = sql_client.instances.ReleaseSsrsLease(request)
operation_ref = client.resource_parser.Create(
'sql.operations',
operation=result.operationId,
project=instance_ref.project)
operations.OperationsV1Beta4.WaitForOperation(sql_client, operation_ref,
'Releasing SSRS lease')
log.status.write('Successfully released SSRS lease.\n')

View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Resets a Cloud SQL read replica size to its primary storage size."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import exceptions
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import properties
import six.moves.http_client
# TODO(b/265881192): remove Hidden label once we are ready to launch.
@base.Hidden
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class ResetReplicaSize(base.Command):
"""Resets a Cloud SQL read replica storage size to its primary storage size."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'replica',
completer=flags.InstanceCompleter,
help='Cloud SQL read replica ID.',
)
def Run(self, args):
"""Resets a Cloud SQL read replica storage size to its primary storage size.
Args:
args: argparse.Namespace, The arguments with which this command was
invoked.
Returns:
A dict object representing the operations resource describing the reset
replica size operation if the reset was successful.
Raises:
HttpException: A http error response was received while executing an api
request.
ResourceNotFoundError: The SQL instance isn't found.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
validate.ValidateInstanceName(args.replica)
instance_ref = client.resource_parser.Parse(
args.replica,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances',
)
try:
request = sql_messages.SqlProjectsInstancesResetReplicaSizeRequest(
project=instance_ref.project, instance=instance_ref.instance
)
result_operation = sql_client.projects_instances.ResetReplicaSize(request)
operation_ref = client.resource_parser.Create(
'sql.operations',
operation=result_operation.name,
project=instance_ref.project,
)
if args.async_:
return {
'Name': instance_ref.instance,
'Project': instance_ref.project,
'OperationId': result_operation.name,
'Status': result_operation.status,
}
operations.OperationsV1Beta4.WaitForOperation(
sql_client,
operation_ref,
'Resetting the Cloud SQL read replica size.',
)
changed_instance_resource = sql_client.instances.Get(
sql_messages.SqlInstancesGetRequest(
project=instance_ref.project, instance=instance_ref.instance
)
)
return {
'Name': instance_ref.instance,
'Project': instance_ref.project,
'StorageSizeGb': changed_instance_resource.settings.dataDiskSizeGb,
}
except apitools_exceptions.HttpError as error:
if error.status_code == six.moves.http_client.FORBIDDEN:
raise exceptions.ResourceNotFoundError(
"There's no instance found at {} or you're not authorized to "
'access it.'.format(instance_ref.RelativeName()))
raise calliope_exceptions.HttpException(error)

View File

@@ -0,0 +1,228 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Reset SSL materials according to the reset SSL mode."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class ResetSslConfigBase(base.Command):
"""Reset SSL materials according to the reset SSL mode."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
AddBaseArgs(parser)
def Run(self, args):
"""Reset SSL materials according to the reset SSL mode.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the
resetSslConfig operation if the reset was successful.
"""
return RunBaseResetSslConfigCommand(args, self.ReleaseTrack())
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class ResetSslConfigBeta(base.Command):
"""Reset SSL materials according to the reset SSL mode."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
AddBaseArgs(parser)
AddBetaArgs(parser)
def Run(self, args):
"""Reset SSL materials according to the reset SSL mode.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the
resetSslConfig operation if the reset was successful.
"""
return RunBaseResetSslConfigCommand(args, self.ReleaseTrack())
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class ResetSslConfigAlpha(base.Command):
"""Reset SSL materials according to the reset SSL mode."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
AddBaseArgs(parser)
AddBetaArgs(parser)
AddAlphaArgs(parser)
def Run(self, args):
"""Reset SSL materials according to the reset SSL mode.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the
resetSslConfig operation if the reset was successful.
"""
return RunBaseResetSslConfigCommand(args, self.ReleaseTrack())
def RunBaseResetSslConfigCommand(args, release_track):
"""Reset SSL materials according to the reset SSL mode.
Args:
args: argparse.Namespace, The arguments that this command was invoked with.
release_track: string, The release track GA/BETA/ALPHA.
Returns:
A dict object representing the operations resource describing the
resetSslConfig operation if the reset was successful.
"""
client = api_util.SqlClient(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',
)
req = sql_messages.SqlInstancesResetSslConfigRequest(
project=instance_ref.project, instance=instance_ref.instance
)
prompt_msg = (
'Resetting your SSL configuration will delete all client certificates and'
' generate a new server certificate.'
)
if args.IsSpecified('mode'):
req.mode = sql_messages.SqlInstancesResetSslConfigRequest.ModeValueValuesEnum.lookup_by_name(
args.mode.upper()
)
if args.mode == 'SYNC_FROM_PRIMARY':
prompt_msg = (
'Syncing related SSL configs from the primary may cause SSL update'
' operations if needed.'
)
if IsBetaOrNewer(release_track):
pass # future beta arguments go here
console_io.PromptContinue(message=prompt_msg, default=True, cancel_on_no=True)
result_operation = sql_client.instances.ResetSslConfig(req)
operation_ref = client.resource_parser.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, 'Resetting SSL config'
)
log.status.write(
'Reset SSL config for [{resource}].\n'.format(resource=instance_ref)
)
def AddAlphaArgs(unused_parser):
"""Adds alpha args and flags to the parser."""
pass
def AddBetaArgs(unused_parser):
"""Adds beta args and flags to the parser."""
pass
def AddBaseArgs(parser):
"""Adds base args and flags to the parser."""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.',
)
parser.add_argument(
'--mode',
choices={
'ALL': 'Refresh all TLS configs. This is the default behaviour.',
'SYNC_FROM_PRIMARY': (
'Refreshes the replication-related TLS configuration settings'
' provided by the primary instance. Not applicable to'
' on-premises replication instances.'
),
},
required=False,
default=None,
help='Selectively refresh the SSL materials',
)
def IsBetaOrNewer(release_track):
"""Returns true if the release track is beta or newer."""
return (
release_track == base.ReleaseTrack.BETA
or release_track == base.ReleaseTrack.ALPHA
)

View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Restarts a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Restart(base.Command):
"""Restarts a Cloud SQL instance."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID.')
def Run(self, args):
"""Restarts a 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 restart
operation if the restart was successful.
"""
client = api_util.SqlClient(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')
console_io.PromptContinue(
message='The instance will shut down and start up again immediately if '
'its activation policy is "always." If "on demand," the instance will '
'start up again when a new connection request is made.',
default=True,
cancel_on_no=True)
result_operation = sql_client.instances.Restart(
sql_messages.SqlInstancesRestartRequest(
project=instance_ref.project, instance=instance_ref.instance))
operation_ref = client.resource_parser.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, 'Restarting Cloud SQL instance')
log.status.write('Restarted [{resource}].\n'.format(resource=instance_ref))

View File

@@ -0,0 +1,132 @@
# -*- coding: utf-8 -*- #
# Copyright 2013 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.
"""Restores a backup of a Cloud SQL instance."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class RestoreBackup(base.Command):
"""Restores a backup of a Cloud SQL instance.
DEPRECATED: This command is deprecated and will be removed.
Use 'gcloud beta sql backups restore' instead.
"""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go
on the command line after this command. Positional arguments are
allowed.
"""
parser.add_argument(
'instance',
completer=flags.InstanceCompleter,
help='Cloud SQL instance ID that will be restored.')
parser.add_argument(
'--backup-id',
type=int,
help='The ID of the backup run to restore from.')
parser.add_argument(
'--backup-instance',
completer=flags.InstanceCompleter,
help='The ID of the instance that the backup was taken from.')
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
"""Restores a backup of a 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
restoreBackup operation if the restoreBackup was successful.
"""
validate.ValidateInstanceName(args.instance)
console_io.PromptContinue(
message=('All current data on the instance will be lost when the '
'backup is restored'),
default=True,
cancel_on_no=True)
return self._HandleBackupId(args)
def _HandleBackupId(self, args):
"""Restores a backup using v1beta4. The backup is specified with backup_id.
Args:
args: argparse.Namespace, The arguments that this command was invoked
with.
Returns:
A dict object representing the operations resource describing the
restoreBackup operation if the restoreBackup was successful.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
instance_ref = client.resource_parser.Parse(
args.instance,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances')
if not args.backup_instance:
args.backup_instance = args.instance
result_operation = sql_client.instances.RestoreBackup(
sql_messages.SqlInstancesRestoreBackupRequest(
project=instance_ref.project,
instance=instance_ref.instance,
instancesRestoreBackupRequest=(
sql_messages.InstancesRestoreBackupRequest(
restoreBackupContext=sql_messages.RestoreBackupContext(
backupRunId=args.backup_id,
instanceId=args.backup_instance,)))))
operation_ref = client.resource_parser.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))
# TODO(b/37302484): Use standard gcloud poller instead of WaitForOperation
operations.OperationsV1Beta4.WaitForOperation(
sql_client, operation_ref, 'Restoring Cloud SQL instance')
log.status.write('Restored [{instance}].\n'.format(instance=instance_ref))
return None

View File

@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*- #
# Copyright 2023 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.
"""Switches over a Cloud SQL instance to one of its replicas.
Switches over a Cloud SQL instance to one of its replicas. Currently only
supported on Cloud SQL for SQL Server and MySQL instances.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import sys
import textwrap
from googlecloudsdk.api_lib.sql import api_util
from googlecloudsdk.api_lib.sql import operations
from googlecloudsdk.api_lib.sql import validate
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.sql import flags
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core.console import console_io
# gcloud timeout for monitoring the switchover LRO
_SWITCHOVER_OPERATION_TIMEOUT_SECONDS = 3600
DETAILED_HELP = {
'EXAMPLES': """\
To switch over an instance to its replica called replica-instance:
$ {command} replica-instance
""",
}
def AddBaseArgs(parser):
"""Declare flag and positional arguments for this command parser."""
base.ASYNC_FLAG.AddToParser(parser)
parser.add_argument(
'replica', completer=flags.InstanceCompleter, help='Cloud SQL replica ID.'
)
flags.AddSwitchoverDbTimeout(parser)
def RunBaseSwitchoverCommand(args):
"""Switches over a Cloud SQL instance to one of its replicas.
Args:
args: argparse.Namespace, The arguments that this command was invoked with.
Returns:
A dict object representing the operations resource describing the
switchover operation if the switchover was successful.
Raises:
exceptions.OperationError: If the switchover operation is not supported for
the instance.
"""
client = api_util.SqlClient(api_util.API_VERSION_DEFAULT)
sql_client = client.sql_client
sql_messages = client.sql_messages
validate.ValidateInstanceName(args.replica)
instance_ref = client.resource_parser.Parse(
args.replica,
params={'project': properties.VALUES.core.project.GetOrFail},
collection='sql.instances',
)
# Format the message ourselves here rather than supplying it as part of the
# 'message' to PromptContinue. Having the whole paragraph be automatically
# formatted by PromptContinue would leave it with a line break in the middle
# of the URL, rendering it unclickable.
sys.stderr.write(
textwrap.TextWrapper().fill(
'Switching over to a replica leads to a short period of downtime'
' and results in the primary and replica instances "switching"'
' roles. Before switching over to the replica, you must verify'
' that both the primary and replica instances are online.'
' Otherwise, use a promote operation.'
)
+ '\n\n'
)
console_io.PromptContinue(message='', default=True, cancel_on_no=True)
db_timeout_str = args.db_timeout
if db_timeout_str is not None:
db_timeout_str = str(args.db_timeout) + 's'
result = sql_client.instances.Switchover(
sql_messages.SqlInstancesSwitchoverRequest(
project=instance_ref.project,
instance=instance_ref.instance,
dbTimeout=db_timeout_str,
)
)
operation_ref = client.resource_parser.Create(
'sql.operations', operation=result.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, 'Switching over to Cloud SQL replica',
_SWITCHOVER_OPERATION_TIMEOUT_SECONDS
)
log.status.write(
'Switched over [{instance}].\n'.format(instance=instance_ref)
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
class Switchover(base.Command):
"""Switches over a Cloud SQL instance to one of its replicas.
Switches over a Cloud SQL instance to one of its replicas.
"""
detailed_help = DETAILED_HELP
def Run(self, args):
return RunBaseSwitchoverCommand(args)
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command. Positional arguments are allowed.
"""
AddBaseArgs(parser)