364 lines
13 KiB
Python
364 lines
13 KiB
Python
# -*- 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)
|