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,42 @@
# -*- 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.
"""Command group for spanner."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
def _GetUri(resource):
return 'https://spanner.googleapis.com/{}/{}'.format('v1', resource.name)
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA,
base.ReleaseTrack.ALPHA)
class Spanner(base.Group):
"""Command groups for Cloud Spanner."""
category = base.DATABASES_CATEGORY
@staticmethod
def Args(parser):
parser.display_info.AddUriFunc(_GetUri)
def Filter(self, context, args):
# TODO(b/190539984): Determine if command group works with project number
base.RequireProjectID(args)
del context, args

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for spanner backup schedules."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class BackupSchedules(base.Group):
"""Manage Cloud Spanner backup schedules."""
pass

View File

@@ -0,0 +1,43 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Cloud Spanner backup schedule.
description: |
Add an IAM policy binding to a Cloud Spanner backup schedule. One binding consists of
a member, a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of 'roles/editor' for the user
'test-user@gmail.com', run:
$ {command} backup-schedule-id \
--instance=instance-id \
--database=database-id \
--member='user:test-user@gmail.com' \
--role='roles/editor'
To add an IAM policy binding which expires at the end of the year 2025 for the role of
'roles/editor' and the user 'test-user@gmail.com', run:
$ {command} backup-schedule-id \
--instance=instance-id \
--database=database-id \
--member='user:test-user@gmail.com' \
--role='roles/editor' \
--condition='expression=request.time < timestamp("2026-01-01T00:00:00Z"),title=expires_end_of_2025,description=Expires at midnight on 2025-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances.databases.backupSchedules
arguments:
resource:
help_text: The Cloud Spanner backup schedule to which to add the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backupSchedule
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,126 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner backup schedule create."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_schedules
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Create(base.CreateCommand):
"""Create a Cloud Spanner backup schedule."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To create a Cloud Spanner backup schedule, run:
$ {command} backup-schedule-id --instance=instance-id --database=database-id --cron="0 2 * * *" --retention-duration=2w --backup-type=full-backup
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear- pair
for that argument which can follow the argument itself.
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.
"""
resource_args.AddBackupScheduleResourceArg(parser, 'to create')
backup_type_choices = ['full-backup', 'incremental-backup']
parser.add_argument(
'--backup-type',
metavar='BACKUP_TYPE',
required=True,
help=("""\
Type of backups created by this schedule.
Supported backup types:
`full-backup`
A full backup stores the entire contents of the database at a given version time.
`incremental-backup`
An incremental backup contains only the data that has changed since a previous backup.
"""),
type=arg_parsers.ArgList(
choices=backup_type_choices,
max_length=1,
),
)
parser.add_argument(
'--cron',
required=True,
help=(
'Textual representation of the crontab. User can customize the'
' backup frequency and the backup version time using the cron'
' expression. The version time must be in UTC timzeone. The backup'
' will contain an externally consistent copy of the database at the'
' version time. Allowed frequencies are 12 hour, 1 day, 1 week and'
' 1 month. Examples of valid cron specifications: * `0 2/12 * * * `'
' : every 12 hours at (2, 14) hours past midnight in UTC. * `0 2,14'
' * * * ` : every 12 hours at (2,14) hours past midnight in UTC. *'
' `0 2 * * * ` : once a day at 2 past midnight in UTC. * `0 2 * * 0'
' ` : once a week every Sunday at 2 past midnight in UTC. * `0 2 8'
' * * ` : once a month on 8th day at 2 past midnight in UTC.'
),
)
parser.add_argument(
'--retention-duration',
required=True,
help=(
'The retention duration of a backup that must be at least 6 hours'
' and at most 366 days. The backup is eligible to be automatically'
' deleted once the retention period has elapsed.'
),
)
encryption_group_parser = parser.add_argument_group()
resource_args.AddCreateBackupEncryptionConfigTypeArg(
encryption_group_parser
)
resource_args.AddKmsKeyResourceArg(
encryption_group_parser, 'to create the Cloud Spanner backup'
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
backup_schedule_ref = args.CONCEPTS.backup_schedule.Parse()
encryption_type = resource_args.GetCreateBackupEncryptionConfigType(args)
kms_key = resource_args.GetAndValidateKmsKeyName(args)
return backup_schedules.Create(
backup_schedule_ref, args, encryption_type, kms_key
)

View File

@@ -0,0 +1,76 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner backup schedule delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_schedules
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Delete(base.DeleteCommand):
"""Delete a Cloud Spanner backup schedule."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To delete a Cloud Spanner backup schedule, run:
$ {command} backup-schedule-id --instance=instance-id --database=database-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear- pair
for that argument which can follow the argument itself.
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.
"""
resource_args.AddBackupScheduleResourceArg(parser, 'to delete')
parser.display_info.AddCacheUpdater(flags.BackupScheduleCompleter)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
backup_schedule_ref = args.CONCEPTS.backup_schedule.Parse()
console_io.PromptContinue(
message='Delete backup schedule [{0}]. Are you sure?'.format(
args.backup_schedule
),
cancel_on_no=True,
)
return backup_schedules.Delete(backup_schedule_ref)

View File

@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner backup schedules describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_schedules
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Describe(base.DescribeCommand):
"""Describe a Cloud Spanner backup schedule."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To describe a Cloud Spanner backup schedule, run:
$ {command} backup-schedule-id --instance=instance-id --database=database-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear- pair
for that argument which can follow the argument itself.
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.
"""
resource_args.AddBackupScheduleResourceArg(parser, 'to describe')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return backup_schedules.Get(args.CONCEPTS.backup_schedule.Parse())

View File

@@ -0,0 +1,23 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Get the IAM policy for a Cloud Spanner backup schedule.
description: |
*{command}* displays the IAM policy associated with a Cloud Spanner
backup schedule. If formatted as JSON, the output can be edited and used as
a policy file for *set-iam-policy*. The output includes an "etag"
field identifying the version emitted and allowing detection of
concurrent policy updates; see
$ {parent} set-iam-policy for additional details.
examples: |
To print the IAM policy for a given Cloud Spanner backup schedule, run:
$ {command} backup-schedule-id --instance=instance-id --database=database-id
request:
collection: spanner.projects.instances.databases.backupSchedules
arguments:
resource:
help_text: The Cloud Spanner backup schedule for which to display the IAM policy.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backupSchedule

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner backup schedule list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_schedules
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
def _TransformBackupTypeSpec(schedule):
"""Transforms the backup type spec field to a human readable string."""
if 'fullBackupSpec' in schedule:
return 'FULL'
elif 'incrementalBackupSpec' in schedule:
return 'INCREMENTAL'
# This should never happen.
return 'UNSPECIFIED'
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class List(base.ListCommand):
"""List Cloud Spanner backup schedules."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To list Cloud Spanner backup schedules, run:
$ {command} --instance=instance-id --database=database-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear- pair
for that argument which can follow the argument itself.
Args:
parser: An argparse parser that you can use to add arguments that go on
the command line after this command.
"""
resource_args.AddDatabaseResourceArg(
parser, 'in which to list schedules', positional=False)
parser.display_info.AddFormat("""
table(
name.basename(),
backup_type_spec():label=BACKUP_TYPE,
spec.cronSpec.text:label=CRON,
retentionDuration,
encryptionConfig.encryptionType,
encryptionConfig.kmsKeyName,
encryptionConfig.kmsKeyNames
)
""")
parser.display_info.AddTransforms({
'backup_type_spec': _TransformBackupTypeSpec,
})
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return backup_schedules.List(args.CONCEPTS.database.Parse())

View File

@@ -0,0 +1,43 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Cloud Spanner backup schedule.
description: |
Remove an IAM policy binding of a Cloud Spanner backup schedule. One binding
consists of a member, a role, and an optional condition.
examples: |
To remove an IAM policy binding for the role of 'roles/editor' for the user
'test-user@gmail.com', run:
$ {command} backup-schedule-id \
--instance=instance-id \
--database=database-id \
--member='user:test-user@gmail.com' \
--role='roles/editor'
To remove an IAM policy binding which expires at the end of the year 2025 for the role of
'roles/editor' and the user 'test-user@gmail.com', run:
$ {command} backup-schedule-id \
--instance=instance-id \
--database=database-id \
--member='user:test-user@gmail.com' \
--role='roles/editor' \
--condition='expression=request.time < timestamp("2026-01-01T00:00:00Z"),title=expires_end_of_2025,description=Expires at midnight on 2025-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances.databases.backupSchedules
arguments:
resource:
help_text: The Cloud Spanner backup schedule to remove the IAM policy binding from.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backupSchedule
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,22 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Set the IAM policy for a Cloud Spanner backup schedule.
description: |
Set the IAM policy for a Cloud Spanner backup schedule given a backup schedule ID and
a file encoded in JSON or YAML that contains the IAM policy.
examples: |
The following command reads an IAM policy defined in a JSON file
`policy.json` and sets it for a Cloud Spanner backup schedule:
$ {command} backup-schedule-id --instance=instance-id --database=database-id policy.json
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
request:
collection: spanner.projects.instances.databases.backupSchedules
arguments:
resource:
help_text: The Cloud Spanner backup schedule to set the IAM policy for.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backupSchedule

View File

@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner backup schedule update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_schedules
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Create(base.UpdateCommand):
"""Update a Cloud Spanner backup schedule."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To update a Cloud Spanner backup schedule, run:
$ {command} backup-schedule-id --instance=instance-id --database=database-id --cron="0 2 * * *" --retention-duration=2w --encryption-type=GOOGLE_DEFAULT_ENCRYPTION
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear- pair
for that argument which can follow the argument itself.
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.
"""
resource_args.AddBackupScheduleResourceArg(parser, 'to create')
group_parser = parser.add_argument_group(required=True)
group_parser.add_argument(
'--cron',
required=False,
help=(
'Textual representation of the crontab. User can customize the'
' backup frequency and the backup version time using the cron'
' expression. The version time must be in UTC timzeone. The backup'
' will contain an externally consistent copy of the database at the'
' version time. Allowed frequencies are 12 hour, 1 day, 1 week and'
' 1 month. Examples of valid cron specifications: * `0 2/12 * * * `'
' : every 12 hours at (2, 14) hours past midnight in UTC. * `0 2,14'
' * * * ` : every 12 hours at (2,14) hours past midnight in UTC. *'
' `0 2 * * * ` : once a day at 2 past midnight in UTC. * `0 2 * * 0'
' ` : once a week every Sunday at 2 past midnight in UTC. * `0 2 8'
' * * ` : once a month on 8th day at 2 past midnight in UTC.'
),
)
group_parser.add_argument(
'--retention-duration',
required=False,
help=(
'The retention duration of a backup that must be at least 6 hours'
' and at most 366 days. The backup is eligible to be automatically'
' deleted once the retention period has elapsed.'
),
)
resource_args.AddCreateBackupEncryptionConfigTypeArg(
group_parser
)
resource_args.AddKmsKeyResourceArg(
group_parser, 'to create the Cloud Spanner backup'
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
backup_schedule_ref = args.CONCEPTS.backup_schedule.Parse()
encryption_type = resource_args.GetCreateBackupEncryptionConfigType(args)
kms_key = resource_args.GetAndValidateKmsKeyName(args)
return backup_schedules.Update(
backup_schedule_ref, args, encryption_type, kms_key
)

View File

@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*- #
# Copyright 2019 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.
"""The backups command group for spanner."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA,
base.ReleaseTrack.GA)
class Backups(base.Group):
"""Manage Cloud Spanner backups."""

View File

@@ -0,0 +1,42 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Cloud Spanner backup.
description: |
Add an IAM policy binding to a Cloud Spanner backup. One binding consists of
a member, a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of 'roles/spanner.backupAdmin' for the user
'test-user@gmail.com' with backup 'example-backup' and instance 'example-instance', run:
$ {command} example-backup \
--instance='example-instance' \
--member='user:test-user@gmail.com' \
--role='roles/spanner.backupAdmin'
To add an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/spanner.backupAdmin' and the user 'test-user@gmail.com' with backup 'example-backup'
and instance 'example-instance', run:
$ {command} example-backup \
--instance='example-instance' \
--member='user:test-user@gmail.com' \
--role='roles/spanner.backupAdmin' \
--condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances.backups
arguments:
resource:
help_text: The Cloud Spanner backup to which to add the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,90 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner backup copy."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_operations
from googlecloudsdk.api_lib.spanner import backups
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core import log
class Copy(base.Command):
"""Copies a backup of a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To copy a backup within the same project, run:
$ {command} --source-instance=SOURCE_INSTANCE_ID --source-backup=SOURCE_BACKUP_ID --destination-instance=DESTINATION_INSTANCE_ID --destination-backup=DESTINATION_BACKUP_ID --expiration-date=2020-03-29T10:49:41Z
To copy a backup to a different project, run:
$ {command} --source-backup=projects/SOURCE_PROJECT_ID/instances/SOURCE_INSTANCE_ID/backups/SOURCE_BACKUP_ID --destination-backup=projects/DESTINATION_PROJECT_ID/instances/DESTINATION_INSTANCE_ID/backups/DESTINATION_BACKUP_ID --expiration-date=2020-03-29T10:49:41Z
"""),
}
@staticmethod
def Args(parser):
"""Register flags for this command."""
resource_args.AddCopyBackupResourceArgs(parser)
group_parser = parser.add_argument_group(mutex=True, required=True)
group_parser.add_argument(
'--expiration-date',
help='Expiration time of the backup, must be at least 6 hours and at '
'most 366 days from the time when the source backup is created. See '
'`$ gcloud topic datetimes` for information on date/time formats.')
group_parser.add_argument(
'--retention-period',
help='Retention period of the backup relative from now, must be at '
'least 6 hours and at most 366 days from the time when the source '
'backup is created. See `$ gcloud topic datetimes` for information '
'on duration formats.')
base.ASYNC_FLAG.AddToParser(parser)
encryption_group_parser = parser.add_argument_group()
resource_args.AddCopyBackupEncryptionTypeArg(encryption_group_parser)
resource_args.AddKmsKeyResourceArg(encryption_group_parser,
'to copy the Cloud Spanner backup')
def Run(self, args):
"""This is what gets called when the user runs this command."""
source_backup_ref = args.CONCEPTS.source.Parse()
destination_backup_ref = args.CONCEPTS.destination.Parse()
encryption_type = resource_args.GetCopyBackupEncryptionType(args)
kms_key = resource_args.GetAndValidateKmsKeyName(args)
op = backups.CopyBackup(source_backup_ref, destination_backup_ref, args,
encryption_type, kms_key)
if args.async_:
log.status.Print('Copy request issued from [{}] to [{}]\n'
'Check operation [{}] for status.'.format(
source_backup_ref.RelativeName(),
destination_backup_ref.RelativeName(), op.name))
return op
op_result = backup_operations.Await(
op, 'Waiting for operation [{}] to complete'.format(op.name))
if op.error is None:
log.CreatedResource(op_result)
return op_result

View File

@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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 for spanner backup create."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_operations
from googlecloudsdk.api_lib.spanner import backups
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core import log
class Create(base.CreateCommand):
"""Creates a backup of a Cloud Spanner database.
Creates a backup of a Cloud Spanner database.
"""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To create a backup asynchronously, run:
$ {command} BACKUP_ID --instance=INSTANCE_NAME --database=DATABASE --expiration-date=2020-03-29T10:49:41Z --async
To create a backup synchronously, run:
$ {command} BACKUP_ID --instance=INSTANCE_NAME --database=DATABASE --retention-period=2w
"""),
}
@staticmethod
def Args(parser):
"""Register flags for this command."""
resource_args.AddBackupResourceArg(parser, 'to create')
parser.add_argument(
'--database',
required=True,
help='ID of the database from which the backup will be created.')
group_parser = parser.add_argument_group(mutex=True, required=True)
group_parser.add_argument(
'--expiration-date',
help='Expiration time of the backup, must be at least 6 hours and at '
'most 30 days from the time the request is received. See '
'`$ gcloud topic datetimes` for information on date/time formats.')
group_parser.add_argument(
'--retention-period',
help='Retention period of the backup relative from now, must be at '
'least 6 hours and at most 30 days. See `$ gcloud topic '
'datetimes` for information on duration formats.')
parser.add_argument(
'--version-time',
metavar='TIMESTAMP',
help='The backup will contain an externally consistent copy of the '
'database at the timestamp specified by `--version-time`. If '
'`--version-time` is not specified, the system will use the creation '
'time of the backup.')
base.ASYNC_FLAG.AddToParser(parser)
encryption_group_parser = parser.add_argument_group()
resource_args.AddCreateBackupEncryptionTypeArg(encryption_group_parser)
resource_args.AddKmsKeyResourceArg(encryption_group_parser,
'to create the Cloud Spanner backup')
def Run(self, args):
"""This is what gets called when the user runs this command."""
backup_ref = args.CONCEPTS.backup.Parse()
encryption_type = resource_args.GetCreateBackupEncryptionType(args)
kms_key = resource_args.GetAndValidateKmsKeyName(args)
op = backups.CreateBackup(backup_ref, args, encryption_type, kms_key)
if args.async_:
log.status.Print('Create request issued for: [{}]\n'
'Check operation [{}] for status.'.format(
args.backup, op.name))
return op
op_result = backup_operations.Await(
op, 'Waiting for operation [{}] to complete'.format(op.name))
if op.error is None:
log.CreatedResource(op_result)
return op_result

View File

@@ -0,0 +1,18 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Delete an existing backup.
description: Delete an existing backup.
examples: |
To delete a backup, run:
$ {command} BACKUP_NAME --instance=INSTANCE_NAME
request:
collection: spanner.projects.instances.backups
modify_request_hooks:
- googlecloudsdk.api_lib.spanner.backups:CheckBackupExists
arguments:
resource:
help_text: Cloud Spanner backup to delete.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup

View File

@@ -0,0 +1,17 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Retrieves information about a backup.
description: Retrieves information about a backup.
examples: |
To describe a backup, run:
$ {command} BACKUP_ID --instance=INSTANCE_NAME
request:
collection: spanner.projects.instances.backups
arguments:
resource:
help_text: Cloud Spanner backup to describe.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup

View File

@@ -0,0 +1,23 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Get the IAM policy for a Cloud Spanner backup.
description: |
*{command}* displays the IAM policy associated with a Cloud Spanner
database. If formatted as JSON, the output can be edited and used as
a policy file for *set-iam-policy*. The output includes an "etag"
field identifying the version emitted and allowing detection of
concurrent policy updates; see
$ {parent} set-iam-policy for additional details.
examples: |
To print the IAM policy for a given Cloud Spanner backup, run:
$ {command} example-backup --instance=example-instance
request:
collection: spanner.projects.instances.backups
arguments:
resource:
help_text: The Cloud Spanner backup for which to display the IAM policy.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup

View File

@@ -0,0 +1,85 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: List existing Cloud Spanner Cloud Spanner backups.
description: List existing Cloud Spanner Cloud Spanner backups.
examples: |
To list existing backups for the instance, run:
$ {command} --instance=INSTANCE_NAME
To list existing backups for a database, run:
$ {command} --instance=INSTANCE_NAME --database=DATABASE
request:
collection: spanner.projects.instances.backups
modify_request_hooks:
- googlecloudsdk.api_lib.spanner.backups:ModifyListRequest
arguments:
params:
- arg_name: database
api_field: filter
required: false
help_text: |
ID of the source database. The database flag will take precedence over filters added for
database.
resource:
help_text: Cloud Spanner instance ID.
spec: !REF googlecloudsdk.command_lib.spanner.resources:instance
response:
modify_response_hooks:
- googlecloudsdk.api_lib.spanner.backups:FormatListBackups
# TODO: b/358133268 - Remove the alpha and beta track overrides once we have the instance
# partitions column in the GA track.
output:
ALPHA:
format: |
table(
name.basename(): label=BACKUP,
database.basename(): label=SOURCE_DATABASE,
versionTime: label=VERSION_TIME,
createTime: label=CREATION_TIME,
expireTime: label=EXPIRATION_TIME,
state: label=STATE,
backupSchedules.join(", "): label=BACKUP_SCHEDULES,
instancePartitions.instancePartition.join(", "): label=INSTANCE_PARTITIONS,
encryptionInfo.encryptionType: label=ENCRYPTION_TYPE,
sizeBytes: label=BACKUP_SIZE_BYTES,
exclusiveSizeBytes: label=EXCLUSIVE_SIZE_BYTES,
oldestVersionTime: label=OLDEST_VERSION_TIME
)
BETA:
format: |
table(
name.basename(): label=BACKUP,
database.basename(): label=SOURCE_DATABASE,
versionTime: label=VERSION_TIME,
createTime: label=CREATION_TIME,
expireTime: label=EXPIRATION_TIME,
state: label=STATE,
backupSchedules.join(", "): label=BACKUP_SCHEDULES,
instancePartitions.instancePartition.join(", "): label=INSTANCE_PARTITIONS,
encryptionInfo.encryptionType: label=ENCRYPTION_TYPE,
sizeBytes: label=BACKUP_SIZE_BYTES,
exclusiveSizeBytes: label=EXCLUSIVE_SIZE_BYTES,
oldestVersionTime: label=OLDEST_VERSION_TIME
)
GA:
format: |
table(
name.basename(): label=BACKUP,
database.basename(): label=SOURCE_DATABASE,
versionTime: label=VERSION_TIME,
createTime: label=CREATION_TIME,
expireTime: label=EXPIRATION_TIME,
state: label=STATE,
backupSchedules.join(", "): label=BACKUP_SCHEDULES,
encryptionInfo.encryptionType: label=ENCRYPTION_TYPE,
sizeBytes: label=BACKUP_SIZE_BYTES,
exclusiveSizeBytes: label=EXCLUSIVE_SIZE_BYTES,
oldestVersionTime: label=OLDEST_VERSION_TIME
)

View File

@@ -0,0 +1,42 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Cloud Spanner backup.
description: |
Remove an IAM policy binding of a Cloud Spanner backup. One binding
consists of a member, a role, and an optional condition.
examples: |
To remove an IAM policy binding for the role of 'roles/spanner.backupAdmin' for the user
'test-user@gmail.com' with backup 'example-backup' and instance 'example-instance', run:
$ {command} example-backup \
--instance='example-instance' \
--member='user:test-user@gmail.com' \
--role='roles/spanner.backupAdmin'
To remove an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/spanner.backupAdmin' and the user 'test-user@gmail.com' with backup 'example-backup'
and instance 'example-instance', run:
$ {command} example-backup \
--instance='example-instance' \
--member='user:test-user@gmail.com' \
--role='roles/spanner.backupAdmin' \
--condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances.backups
arguments:
resource:
help_text: The Cloud Spanner backup to remove the IAM policy binding from.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,23 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Set the IAM policy for a Cloud Spanner backup.
description: |
Set the IAM policy for a Cloud Spanner backup given a backup ID and
a file encoded in JSON or YAML that contains the IAM policy.
examples: |
The following command reads an IAM policy defined in a JSON file
`policy.json` and sets it for a spanner instance with the ID
`example-instance`:
$ {command} example-backup --instance=example-instance policy.json
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
request:
collection: spanner.projects.instances.backups
arguments:
resource:
help_text: The Cloud Spanner backup to set the IAM policy for.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup

View File

@@ -0,0 +1,40 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Updates the metadata of a Cloud Spanner a backup.
description: Updates the metadata of a Cloud Spanner a backup.
examples: |
To update the backup metadata with an exact expiration date, run:
$ {command} BACKUP_ID --instance=INSTANCE_NAME --expiration-date=2020-03-29T10:49:41Z
To update the backup metadata with a retention period, run:
$ {command} BACKUP_ID --instance=INSTANCE_NAME --retention-period=2w
request:
collection: spanner.projects.instances.backups
method: 'patch'
modify_request_hooks:
- googlecloudsdk.api_lib.spanner.backups:ModifyUpdateMetadataRequest
arguments:
params:
- group:
mutex: true
required: true
params:
- arg_name: expiration-date
api_field: backup.expireTime
help_text: |
Expiration time of the backup, must be at least 6 hours and at most 366 days from
the time of creation. See `$ gcloud topic datetimes` for information on
date/time formats.
- arg_name: retention-period
api_field: backup.expireTime
help_text: |
Retention period of the backup relative from now, must be at least 6 hours and at most
a year from the time of creation. See `$ gcloud topic datetimes` for information on
duration formats.
resource:
help_text: The Cloud Spanner backup to update.
spec: !REF googlecloudsdk.command_lib.spanner.resources:backup

View File

@@ -0,0 +1,112 @@
# -*- 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.
"""Start the Spanner command-line interface."""
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import cli_backend
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.core import properties
DETAILED_HELP = {
"EXAMPLES": """\
To start an interactive shell with your Spanner example database, run the following command:
$ {command} example-database --instance=example-instance
""",
}
def AddBaseArgs(parser):
"""Parses provided arguments to add base arguments.
Args:
parser: an argparse argument parser.
"""
resource_args.AddDatabaseResourceArg(
parser, "to use within the interactive shell"
)
flags.GetSpannerCliDatabaseRoleFlag().AddToParser(parser)
flags.GetSpannerCliDelimiterFlag().AddToParser(parser)
flags.GetSpannerCliExecuteFlag().AddToParser(parser)
flags.GetSpannerCliHostFlag().AddToParser(parser)
flags.GetSpannerCliHtmlFlag().AddToParser(parser)
flags.GetSpannerCliIdleTransactionTimeoutFlag().AddToParser(parser)
flags.GetSpannerCliInitCommandAddFlag().AddToParser(parser)
flags.GetSpannerCliInitCommandFlag().AddToParser(parser)
flags.GetSpannerCliPortFlag().AddToParser(parser)
flags.GetSpannerCliPromptFlag().AddToParser(parser)
flags.GetSpannerCliSkipColumnNamesFlag().AddToParser(parser)
flags.GetSpannerCliSkipSystemCommandFlag().AddToParser(parser)
flags.GetSpannerCliSystemCommandFlag().AddToParser(parser)
flags.GetSpannerCliSourceFlag().AddToParser(parser)
flags.GetSpannerCliTableFlag().AddToParser(parser)
flags.GetSpannerCliTeeFlag().AddToParser(parser)
flags.GetSpannerCliXmlFlag().AddToParser(parser)
flags.GetSpannerCliDirectedReadCommandFlag().AddToParser(parser)
flags.GetSpannerCliProtoDescriptorFileCommandFlag().AddToParser(parser)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.GA)
class Cli(base.BinaryBackedCommand):
"""An interactive shell for Spanner."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""See base class."""
AddBaseArgs(parser)
def Run(self, args):
project = arg_utils.GetFromNamespace(args, "--project", use_defaults=True)
instance = args.CONCEPTS.database.Parse().Parent().Name()
api_endpoint_override = properties.VALUES.api_endpoint_overrides.Property(
"spanner"
).Get()
# Create the command executor.
command_executor = cli_backend.SpannerCliWrapper()
env_vars = cli_backend.GetEnvArgsForCommand()
command_executor(
project=project,
database=args.database,
instance=instance,
database_role=args.database_role,
host=args.host,
port=args.port,
api_endpoint=api_endpoint_override,
idle_transaction_timeout=args.idle_transaction_timeout,
skip_column_names=args.skip_column_names,
skip_system_command=args.skip_system_command,
system_command=args.system_command,
prompt=args.prompt,
delimiter=args.delimiter,
table=args.table,
html=args.html,
xml=args.xml,
execute=args.execute,
source=args.source,
tee=args.tee,
init_command=args.init_command,
init_command_add=args.init_command_add,
directed_read=args.directed_read,
proto_descriptor_file=args.proto_descriptor_file,
env=env_vars,
)
pass

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""Command group for spanner databases."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Databases(base.Group):
"""Manage Cloud Spanner databases."""
pass

View File

@@ -0,0 +1,34 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Cloud Spanner database.
description: |
Add an IAM policy binding to a Cloud Spanner database. One binding consists of a member,
a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of 'roles/editor' for the user 'test-user@gmail.com'
with database 'my-database' and instance 'my-instance', run:
$ {command} my-database --instance='my-instance' --member='user:test-user@gmail.com' --role='roles/editor'
To add an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/spanner.databaseAdmin' and the user 'test-user@gmail.com' with database 'my-database' and instance 'my-instance', run:
$ {command} my-database --instance='my-instance' --member='user:test-user@gmail.com' --role='roles/spanner.databaseAdmin' --condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances.databases
arguments:
resource:
help_text: The Cloud Spanner database to which to add the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.spanner.resources:database
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,108 @@
# -*- 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.
"""Command for spanner database change quorum."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class ChangeQuorum(base.Command):
"""Change quorum of a Cloud Spanner database."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To trigger change quorum from single-region mode to dual-region mode, run:
$ {command} my-database-id --instance=my-instance-id --dual-region
To trigger change quorum from dual-region mode to single-region mode with serving location as `asia-south1`, run:
$ {command} my-database-id --instance=my-instance-id --single-region --serving-location=asia-south1
To trigger change quorum using etag specified, run:
$ {command} my-database-id --instance=my-instance-id --dual-region --etag=ETAG
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'to change quorum')
dual_region_or_single_region = parser.add_mutually_exclusive_group(
required=True
)
dual_region_flags = dual_region_or_single_region.add_argument_group(
'Command-line flag for dual-region quorum change:'
)
dual_region_flags.add_argument(
'--dual-region',
required=True,
action='store_true',
help='Switch to dual-region quorum type.',
)
single_region_flags = dual_region_or_single_region.add_argument_group(
'Command-line flags for single-region quorum change:'
)
single_region_flags.add_argument(
'--single-region',
required=True,
action='store_true',
help='Switch to single-region quorum type.',
)
single_region_flags.add_argument(
'--serving-location',
required=True,
help='The cloud Spanner location.',
)
parser.add_argument(
'--etag', help='Used for optimistic concurrency control.'
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
msgs = apis.GetMessagesModule('spanner', 'v1')
if args.dual_region:
quorum_type = msgs.QuorumType(dualRegion=msgs.DualRegionQuorum())
else:
quorum_type = msgs.QuorumType(
singleRegion=msgs.SingleRegionQuorum(
servingLocation=args.serving_location
)
)
return databases.ChangeQuorum(
args.CONCEPTS.database.Parse(), quorum_type, args.etag
)

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 Spanner database 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 Spanner database configurations."""

View File

@@ -0,0 +1,38 @@
release_tracks: [ALPHA]
command_type: CONFIG_EXPORT
help_text:
brief: Export the configuration for a Spanner database.
description: |
*{command}* exports the configuration for a Spanner database.
Database 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
databases within the project.
Specifying `--path` allows you to export the configuration(s) to
a local directory.
examples: |
To export the configuration for a database, run:
$ {command} my-database
To export the configuration for a database to a file, run:
$ {command} my-database --path=/path/to/dir/
To export the configuration for a database in Terraform
HCL format, run:
$ {command} my-database --resource-format=terraform
To export the configurations for all databases within a
project, run:
$ {command} --all
arguments:
resource:
help_text: Database to export the configuration for.
spec: !REF googlecloudsdk.command_lib.spanner.resources:database

View File

@@ -0,0 +1,109 @@
# -*- 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.
"""Command for spanner databases create."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
class Create(base.CreateCommand):
"""Create a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To create an empty Cloud Spanner database, run:
$ {command} testdb --instance=my-instance-id
To create a Cloud Spanner database with populated schema, run:
$ {command} testdb --instance=my-instance-id
--ddl='CREATE TABLE mytable (a INT64, b INT64) PRIMARY KEY(a)'
To create a Cloud Spanner database with the PostgreSQL dialect, run:
$ {command} testdb --instance=my-instance-id
--database-dialect=POSTGRESQL
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'to create')
flags.Ddl(help_text='Semi-colon separated DDL (data definition language) '
'statements to run inside the '
'newly created database. If there is an error in any statement, '
'the database is not created. This option is not supported for '
'the PostgreSQL dialect. Full DDL specification is at '
'https://cloud.google.com/spanner/docs/data-definition-language'
).AddToParser(parser)
flags.DdlFile(
help_text='Path of a file that contains semi-colon separated DDL (data '
'definition language) statements to run inside the newly created '
'database. If there is an error in any statement, the database is not '
'created. This option is not supported for the PostgreSQL dialect. '
'Full DDL specification is at '
'https://cloud.google.com/spanner/docs/data-definition-language.'
' If --ddl_file is set, --ddl is ignored. One line comments starting '
'with -- are ignored.').AddToParser(parser)
flags.ProtoDescriptorsFile(
help_text='Path of a file that contains a protobuf-serialized '
'google.protobuf.FileDescriptorSet message. To generate it, install and'
' run `protoc` with --include_imports and --descriptor_set_out.'
).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.DatabaseCompleter)
resource_args.AddKmsKeyResourceArg(parser,
'to create the Cloud Spanner database')
flags.DatabaseDialect(
help_text='The SQL dialect of the Cloud Spanner Database. '
'GOOGLE_STANDARD_SQL is the default.'
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
database_ref = args.CONCEPTS.database.Parse()
instance_ref = database_ref.Parent()
kms_key_name_or_names = resource_args.GetAndValidateKmsKeyName(args)
op = databases.Create(
instance_ref,
args.database,
flags.SplitDdlIntoStatements(args),
flags.GetProtoDescriptors(args),
kms_key_name_or_names,
args.database_dialect,
)
if args.async_:
return op
return database_operations.Await(op, 'Creating database')

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""Command group for spanner databases ddl."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Ddl(base.Group):
"""Manage the DDL for Cloud Spanner databases."""
pass

View File

@@ -0,0 +1,68 @@
# -*- 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.
"""Command for spanner databases ddl describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
# TODO(b/305722139) Change @base.DefaultUniverseOnly to
# @base.UniverseCompatible once b/305722139 is fixed.
# See go/gcloud-cli-running-tpc-tests.
@base.DefaultUniverseOnly
class Describe(base.ListCommand):
"""Describe the DDL for a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To describe the DDL for a given Cloud Spanner database, run:
$ {command} my-database-id --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'of which the ddl to describe')
parser.display_info.AddCacheUpdater(None)
parser.display_info.AddFormat('value(format("{0};\n"))')
flags.IncludeProtoDescriptors(
help_text=(
'Include debug string of proto bundle descriptors in the output.'
' The output is information only and not meant to be parsed.'
)
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return databases.GetDdlWithDescriptors(args.CONCEPTS.database.Parse(), args)

View File

@@ -0,0 +1,82 @@
# -*- 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.
"""Command for spanner databases ddl update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core import log
class Update(base.UpdateCommand):
"""Update the DDL for a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To add a column to a table in the given Cloud Spanner database, run:
$ {command} my-database-id --instance=my-instance-id
--ddl='ALTER TABLE test_table ADD COLUMN a INT64'
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'of which the ddl to update')
flags.Ddl(help_text='Semi-colon separated DDL '
'(data definition language) statements to '
'run inside the database. If a statement fails, all subsequent '
'statements in the batch are automatically cancelled.'
).AddToParser(parser)
flags.DdlFile(
help_text='Path of a file containing semi-colon separated DDL (data '
'definition language) statements to run inside the database. If a '
'statement fails, all subsequent statements in the batch are '
'automatically cancelled. If --ddl_file is set, --ddl is ignored. '
'One line comments starting with -- are ignored.').AddToParser(parser)
flags.ProtoDescriptorsFile(
help_text='Path of a file that contains a protobuf-serialized '
'google.protobuf.FileDescriptorSet message. To generate it, install and'
' run `protoc` with --include_imports and --descriptor_set_out.'
).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
op = databases.UpdateDdl(args.CONCEPTS.database.Parse(),
flags.SplitDdlIntoStatements(args),
flags.GetProtoDescriptors(args))
if args.async_:
return log.status.Print(
'Schema update in progress. Operation name={}'.format(op.name))
return database_operations.Await(op, 'Schema updating')

View File

@@ -0,0 +1,78 @@
# -*- 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.
"""Command for spanner databases delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core.console import console_io
class Delete(base.DeleteCommand):
"""Delete a Cloud Spanner database.
Delete a Cloud Spanner database.
Note: Cloud Spanner might continue to accept requests for a few seconds
after the database has been deleted.
"""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To delete a Cloud Spanner database, run:
$ {command} my-database-id --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'to delete')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Database delete response, which is empty.
Raises:
HttpException when the database is not found.
"""
database_ref = args.CONCEPTS.database.Parse()
console_io.PromptContinue(
'You are about to delete database: [{}]'.format(database_ref.Name()),
throw_if_unattended=True,
cancel_on_no=True)
# The delete API returns a 200 regardless of whether the database being
# deleted exists. In order to show users feedback for incorrectly
# entered database names, we have to make a request to check if the database
# exists. If the database exists, it's deleted, otherwise, we display the
# error from databases.Get.
database = databases.Get(database_ref)
if database:
return databases.Delete(database_ref)

View File

@@ -0,0 +1,55 @@
# -*- 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.
"""Command for spanner databases describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
class Describe(base.DescribeCommand):
"""Describe a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To describe a Cloud Spanner database, run:
$ {command} my-database-id --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'to describe')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return databases.Get(args.CONCEPTS.database.Parse())

View File

@@ -0,0 +1,256 @@
# -*- 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.
"""Command for spanner databases query."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exceptions
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.command_lib.spanner import sql
from googlecloudsdk.command_lib.spanner.sql import QueryHasDml
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import resources
DETAILED_HELP = {
'EXAMPLES':
"""\
To execute a SQL SELECT statement against example-database under
example-instance, run:
$ {command} example-database --instance=example-instance --sql='SELECT * FROM MyTable WHERE MyKey = 1'
""",
}
def CreateSession(args, database_role=None):
"""Creates a session.
Args:
args: an argparse namespace. All the arguments that were provided to the
command invocation.
database_role: Cloud Spanner database role which owns this session.
Returns:
A session reference to be used to execute the sql.
"""
session_name = database_sessions.Create(args.CONCEPTS.database.Parse(),
database_role)
return resources.REGISTRY.ParseRelativeName(
relative_name=session_name.name,
collection='spanner.projects.instances.databases.sessions')
def AddBaseArgs(parser):
"""Parses provided arguments to add base arguments used for both Beta and GA.
Args:
parser: an argparse argument parser.
"""
resource_args.AddDatabaseResourceArg(parser,
'to execute the SQL query against')
parser.add_argument(
'--sql',
required=True,
help='The SQL query to issue to the database. Cloud Spanner SQL is '
'described at https://cloud.google.com/spanner/docs/query-syntax')
query_mode_choices = {
'NORMAL': (
'Returns only the query result, without any information about '
'the query plan.'
),
'PLAN': (
'Returns only the query plan, without any result rows or '
'execution statistics information.'
),
'PROFILE': (
'Returns the query plan, overall execution statistics, '
'operator-level execution statistics, along with the result rows.'
),
'WITH_STATS': (
'Returns the overall (but not operator-level) execution statistics '
'along with the results.'
),
'WITH_PLAN_AND_STATS': (
'Returns the query plan, overall (but not operator-level) execution '
'statistics, along with the results.'
),
}
parser.add_argument(
'--query-mode',
default='NORMAL',
type=lambda x: x.upper(),
choices=query_mode_choices,
help='Mode in which the query must be processed.')
parser.add_argument(
'--enable-partitioned-dml',
action='store_true',
help='Execute DML statement using Partitioned DML')
parser.add_argument(
'--timeout',
type=arg_parsers.Duration(),
default='10m',
help='Maximum time to wait for the SQL query to complete. See $ gcloud '
'topic datetimes for information on duration formats.')
msgs = apis.GetMessagesModule('spanner', 'v1')
GetRequestPriorityMapper(msgs).choice_arg.AddToParser(parser)
timestamp_bound_group = parser.add_argument_group(
mutex=True,
help='Read-only query timestamp bound. The default is --strong. See '
'https://cloud.google.com/spanner/docs/timestamp-bounds.')
timestamp_bound_group.add_argument(
'--strong',
action='store_true',
help='Perform a strong query.')
timestamp_bound_group.add_argument(
'--read-timestamp',
metavar='TIMESTAMP',
help='Perform a query at the given timestamp.')
parser.add_argument(
'--database-role',
help='Database role user assumes while accessing the database.')
def GetRequestPriorityMapper(messages):
return arg_utils.ChoiceEnumMapper(
'--priority',
messages.RequestOptions.PriorityValueValuesEnum,
custom_mappings={
'PRIORITY_LOW': 'low',
'PRIORITY_MEDIUM': 'medium',
'PRIORITY_HIGH': 'high',
'PRIORITY_UNSPECIFIED': 'unspecified',
},
help_str='The priority for the execute SQL request.',
)
@base.DefaultUniverseOnly
@base.UnicodeIsSupported
class Query(base.Command):
"""Executes a SQL query against a Cloud Spanner database."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""See base class."""
AddBaseArgs(parser)
def Run(self, args):
"""Runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
msgs = apis.GetMessagesModule('spanner', 'v1')
request_options = msgs.RequestOptions(
priority=GetRequestPriorityMapper(msgs).GetEnumForChoice(args.priority)
)
read_only_options = self.ParseReadOnlyOptions(args)
session = CreateSession(args, args.database_role)
try:
return database_sessions.ExecuteSql(
args.sql,
args.query_mode,
session,
read_only_options,
request_options,
args.enable_partitioned_dml,
args.timeout)
finally:
database_sessions.Delete(session)
def ParseReadOnlyOptions(self, args):
"""Parses the options for a read-only request from command line arguments.
Args:
args: Command line arguments.
Returns:
A ReadOnly message if the query is read-only (not DML), otherwise None.
"""
if QueryHasDml(args.sql):
if args.IsSpecified('strong'):
raise c_exceptions.InvalidArgumentException(
'--strong',
'A timestamp bound cannot be specified for a DML statement.'
)
if args.IsSpecified('read_timestamp'):
raise c_exceptions.InvalidArgumentException(
'--read-timestamp',
'A timestamp bound cannot be specified for a DML statement.'
)
return None
else:
msgs = apis.GetMessagesModule('spanner', 'v1')
if args.IsSpecified('read_timestamp'):
return msgs.ReadOnly(readTimestamp=args.read_timestamp)
elif args.IsSpecified('strong'):
if not args.strong:
raise c_exceptions.InvalidArgumentException(
'--strong',
'`--strong` cannot be set to false. '
'Instead specify a different type of timestamp bound.'
)
else:
return msgs.ReadOnly(strong=True)
else:
# The default timestamp bound is strong.
return msgs.ReadOnly(strong=True)
def Display(self, args, result):
"""Displays the server response to a query.
This is called higher up the stack to over-write default display behavior.
What gets displayed depends on the mode in which the query was run.
'NORMAL': query result rows
'PLAN': query plan without execution statistics
'PROFILE': query result rows and the query plan with execution statistics
Args:
args: The arguments originally passed to the command.
result: The output of the command before display.
"""
display_plan = (
args.query_mode == 'PLAN'
or args.query_mode == 'PROFILE'
or args.query_mode == 'WITH_PLAN_AND_STATS'
)
display_results = args.query_mode != 'PLAN'
if sql.QueryHasAggregateStats(result):
sql.DisplayQueryAggregateStats(result.stats.queryStats, log.out)
if display_plan:
sql.DisplayQueryPlan(result, log.out)
if display_results:
sql.DisplayQueryResults(
result, log.status if args.query_mode == 'PROFILE' else log.out
)

View File

@@ -0,0 +1,58 @@
# -*- 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.
"""Command for spanner databases get-iam-policy."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.ReleaseTracks(base.ReleaseTrack.GA, base.ReleaseTrack.BETA)
class GetIamPolicy(base.ListCommand):
"""Get the IAM policy for a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To print the IAM policy for a given Cloud Spanner database, run:
$ {command} my-database-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser,
'to get IAM policy binding for')
base.URI_FLAG.RemoveFromParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return databases.GetIamPolicy(args.CONCEPTS.database.Parse())

View File

@@ -0,0 +1,23 @@
- release_tracks: [ALPHA]
help_text:
brief: Get the IAM policy for a Cloud Spanner database.
description: |
*{command}* displays the IAM policy associated with a Cloud Spanner
database. If formatted as JSON, the output can be edited and used as
a policy file for *set-iam-policy*. The output includes an "etag"
field identifying the version emitted and allowing detection of
concurrent policy updates; see
$ {parent} set-iam-policy for additional details.
examples: |
To print the IAM policy for a given Cloud Spanner database, run:
$ {command} my-database
request:
collection: spanner.projects.instances.databases
arguments:
resource:
help_text: The Cloud Spanner database for which to display the IAM policy.
spec: !REF googlecloudsdk.command_lib.spanner.resources:database

View File

@@ -0,0 +1,91 @@
# -*- 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.
"""Import data from various source files to Cloud Spanner."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import migration_backend
from googlecloudsdk.command_lib.util.apis import arg_utils
from googlecloudsdk.core.credentials import store
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class Import(base.BinaryBackedCommand):
"""Import data from various source files to Cloud Spanner."""
detailed_help = {
'EXAMPLES':
textwrap.dedent(text="""\
To import data from a source file to Cloud Spanner:
$ {command} --instance=instanceA --database=databaseA
--table-name=tableA --source-uri=gs://bucket/data.csv --source-format=csv
--schema-uri=gs://bucket/schema.json
$ {command} --instance=instanceA --database=databaseA
--source-uri=gs://bucket/dump.sql --source-format=mysqldump
"""),
}
@staticmethod
def Args(parser):
"""Register the flags for this command."""
flags.Instance(False).AddToParser(parser)
flags.Database(False, True).AddToParser(parser)
flags.TableName(False).AddToParser(parser)
flags.SourceUri(True).AddToParser(parser)
flags.SourceFormat(True).AddToParser(parser)
flags.SchemaUri(False).AddToParser(parser)
flags.CsvLineDelimiter(False).AddToParser(parser)
flags.CsvFieldDelimiter(False).AddToParser(parser)
flags.DatabaseDialect('Dialect for the spanner database').AddToParser(
parser
)
def Run(self, args):
"""Run the import command."""
auth_token = store.GetFreshAccessTokenIfEnabled(min_expiry_duration='1h')
command_executor = migration_backend.SpannerMigrationWrapper()
env_vars = migration_backend.GetEnvArgsForCommand(
extra_vars={
'GCLOUD_HB_PLUGIN': 'true',
'GCLOUD_AUTH_PLUGIN': 'true',
'GCLOUD_AUTH_ACCESS_TOKEN': auth_token,
}
)
project = arg_utils.GetFromNamespace(args, '--project', use_defaults=True)
response = command_executor(
command='import',
instance=args.instance,
database=args.database,
table_name=args.table_name,
source_uri=args.source_uri,
source_format=args.source_format,
schema_uri=args.schema_uri,
csv_line_delimiter=args.csv_line_delimiter,
csv_field_delimiter=args.csv_field_delimiter,
project=project,
database_dialect=args.database_dialect,
env=env_vars,
)
self.exit_code = response.exit_code
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,58 @@
# -*- 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.
"""Command for spanner databases list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
class List(base.ListCommand):
"""List the Cloud Spanner databases contained within the given instance."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To list the Cloud Spanner databases in an instance, run:
$ {command} --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddInstanceResourceArg(
parser, 'in which to list databases', positional=False)
parser.display_info.AddFormat("""
table(
name.basename(),
state,
version_retention_period,
earliest_version_time,
encryptionConfig.kmsKeyName,
enable_drop_protection
)
""")
def Run(self, args):
"""This is what gets called when the user runs this command."""
return databases.List(args.CONCEPTS.instance.Parse())

View File

@@ -0,0 +1,33 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Cloud Spanner database.
description: |
Remove an IAM policy binding of a Cloud Spanner database.
examples: |
To remove an IAM policy binding for the role of 'roles/editor' for the user 'test-user@gmail.com'
with database 'my-database' and instance 'my-instance', run:
$ {command} my-database --instance='my-instance' --member='user:test-user@gmail.com' --role='roles/editor'
To remove an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/spanner.databaseAdmin' and the user 'test-user@gmail.com' with database 'my-database' and instance 'my-instance', run:
$ {command} my-database --instance='my-instance' --member='user:test-user@gmail.com' --role='roles/spanner.databaseAdmin' --condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances.databases
arguments:
resource:
help_text: The Cloud Spanner database to remove the IAM policy binding from.
spec: !REF googlecloudsdk.command_lib.spanner.resources:database
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*- #
# Copyright 2020 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 for spanner restore database."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core import log
class Restore(base.RestoreCommand):
"""Restore a Cloud Spanner database."""
detailed_help = {
'DESCRIPTION':
textwrap.dedent("""
Restores from a backup to a new Cloud Spanner database."""),
'EXAMPLES':
textwrap.dedent("""
To restore a backup, run:
$ {command} --source-backup=BACKUP_ID --source-instance=SOURCE_INSTANCE --destination-database=DATABASE --destination-instance=INSTANCE_NAME
To restore a backup using relative names, run:
$ {command} --source-backup=projects/PROJECT_ID/instances/SOURCE_INSTANCE_ID/backups/BACKUP_ID --destination-database=projects/PROJECT_ID/instances/SOURCE_INSTANCE_ID/databases/DATABASE_ID
""")
}
@staticmethod
def Args(parser):
"""Register flags for this command."""
resource_args.AddRestoreResourceArgs(parser)
base.ASYNC_FLAG.AddToParser(parser)
encryption_group_parser = parser.add_argument_group()
resource_args.AddRestoreDbEncryptionTypeArg(encryption_group_parser)
resource_args.AddKmsKeyResourceArg(encryption_group_parser,
'to restore the Cloud Spanner database')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
A message indicating database is restoring or when async, the operation.
"""
backup_ref = args.CONCEPTS.source.Parse()
database_ref = args.CONCEPTS.destination.Parse()
encryption_type = resource_args.GetRestoreDbEncryptionType(args)
kms_key = resource_args.GetAndValidateKmsKeyName(args)
op = databases.Restore(database_ref, backup_ref, encryption_type, kms_key)
if args.async_:
return log.status.Print(
'Restore database in progress. Operation name={}'.format(op.name))
return database_operations.Await(
op,
'Restoring backup {0} to database {1}'.format(backup_ref.Name(),
database_ref.Name()))

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for spanner database roles."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Roles(base.Group):
"""Manage Cloud Spanner database roles."""
pass

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner database roles list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_roles
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
class List(base.ListCommand):
"""List the Cloud Spanner database roles defined in the given database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To list the Cloud Spanner database roles in a database, run:
$ {command} --instance=my-instance-id --database=my-database-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(
parser, 'in which to list database roles', positional=False)
parser.display_info.AddFormat("""
table(
name.basename()
)
""")
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return database_roles.List(args.CONCEPTS.database.Parse())

View File

@@ -0,0 +1,28 @@
# -*- 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.
"""Command group for spanner databases sessions."""
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 Sessions(base.Group):
"""Manage the sessions for Cloud Spanner databases."""
pass

View File

@@ -0,0 +1,56 @@
# -*- 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.
"""Command for spanner database session delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
class Delete(base.DeleteCommand):
"""Delete a Cloud Spanner session."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To delete a Cloud Spanner session, run:
$ {command} my-session-id --instance=my-instance-id
--database=my-database-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddSessionResourceArg(parser, 'to delete')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return database_sessions.Delete(args.CONCEPTS.session.Parse())

View File

@@ -0,0 +1,65 @@
# -*- 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.
"""Command for spanner sessions list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
class List(base.ListCommand):
"""List the Cloud Spanner sessions contained within the given database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To list the sessions for a given Cloud Spanner database, run:
$ {command} --instance=my-instance-id --database=my-database-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(
parser, 'in which to list sessions', positional=False)
parser.add_argument(
'--server-filter',
required=False,
help=
'An expression for filtering the results of the request on the server. '
'Filter rules are case insensitive. The fields eligible for filtering '
'are: * labels.key where key is the name of a label.')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return database_sessions.List(args.CONCEPTS.database.Parse(),
args.server_filter)

View File

@@ -0,0 +1,67 @@
# -*- 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.
"""Command for spanner databases set-iam-policy."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.command_lib.spanner import iam
from googlecloudsdk.command_lib.spanner import resource_args
class SetIamPolicy(base.Command):
"""Set the IAM policy for a Cloud Spanner database."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
The following command reads an IAM policy defined in a JSON file
`policy.json` and sets it for a spanner database with the ID
`my-database-id`:
$ {command} my-database-id --instance=my-instance-id policy.json
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser,
'to set IAM policy binding for')
parser.add_argument(
'policy_file', help='Name of JSON or YAML file with the IAM policy.')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
database_ref = args.CONCEPTS.database.Parse()
result = iam.SetDatabaseIamPolicy(database_ref, args.policy_file)
iam_util.LogSetIamPolicy(database_ref.Name(), 'database')
return result

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for spanner databases splits."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
@base.DefaultUniverseOnly
class Splits(base.Group):
"""Manage the split points for Spanner databases."""
pass

View File

@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner databases add splits."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_splits
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class Add(base.UpdateCommand):
"""Add split points to a Spanner database."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To add split points to the given Spanner database, run:
$ {command} my-database-id --instance=my-instance-id
--splits-file=path/to/splits.txt --initiator=my-initiator-string
--split-expiration-date=2024-08-15T15:55:10Z
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'on which to add split points')
flags.SplitsFile(
help_text=(
'The path of a file containing split points to add to the database.'
' Separate split points in the file with a new line. The file'
' format is <ObjectType>[space]<ObjectName>[space]<Split Value>,'
' where the ObjectType is one of TABLE or INDEX and the Split Value'
' is the split point key. For index, the split point key is the'
' index key with or without a full table key prefix.'
)
).AddToParser(parser)
flags.SplitExpirationDate(
help_text=(
'The date when the split points become system managed and'
' becomes eligible for merging. The default is 10 days from the'
' date of creation. The maximum is 30 days from the date of'
' creation.'
)
).AddToParser(parser)
flags.Initiator(
help_text=(
'The tag to identify the initiator of the split points.'
)
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return database_splits.AddSplitPoints(
args.CONCEPTS.database.Parse(),
flags.GetSplitPoints(args),
args.initiator,
)

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner databases list user splits."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_splits
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA
)
class List(base.ListCommand):
"""List split points that are added by a user to a Spanner database."""
detailed_help = {
'EXAMPLES': textwrap.dedent(text="""\
To list the user added split points of the given Spanner database,
run:
$ {command} my-database-id --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""See base class."""
base.URI_FLAG.RemoveFromParser(parser)
parser.display_info.AddFormat(DEFAULT_SPLIT_POINTS_FORMAT)
resource_args.AddDatabaseResourceArg(
parser, 'on which to list split points')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return database_splits.ListSplitPoints(args.CONCEPTS.database.Parse())
DEFAULT_SPLIT_POINTS_FORMAT = """\
table(
TABLE_NAME,
INDEX_NAME,
INITIATOR,
SPLIT_KEY,
EXPIRE_TIME
)"""

View File

@@ -0,0 +1,84 @@
# -*- 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.
"""Command for spanner databases update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.UniverseCompatible
class Update(base.UpdateCommand):
"""Update a Cloud Spanner database."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To enable database deletion protection on a Cloud Spanner database
'my-database', run:
$ {command} my-database --enable-drop-protection
To disable database deletion protection on a Cloud Spanner database
'my-database', run:
$ {command} my-database --no-enable-drop-protection
To update KMS key references for a Cloud Spanner database
'my-database', run:
$ {command} my-database --kms-keys="KEY1,KEY2"
"""),
}
@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.
"""
resource_args.AddDatabaseResourceArg(parser, 'to update')
group_parser = parser.add_argument_group(mutex=True)
flags.EnableDropProtection().AddToParser(group_parser)
flags.EnableUpdateKmsKeys().AddToParser(group_parser)
base.ASYNC_FLAG.AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs the `database update` command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Database update response.
"""
op = databases.Update(
args.CONCEPTS.database.Parse(),
args.enable_drop_protection,
args.kms_keys,
)
if args.async_:
return op
return database_operations.Await(op, 'Updating database.')

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""Command group for spanner instance configs."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Configs(base.Group):
"""Manage Cloud Spanner instance configs."""
pass

View File

@@ -0,0 +1,204 @@
# -*- 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 for spanner instance configs create."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_config_operations
from googlecloudsdk.api_lib.spanner import instance_configs
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exceptions
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.util.args import labels_util
class Create(base.CreateCommand):
"""Create a Cloud Spanner instance configuration."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To create a custom Cloud Spanner instance configuration based on an existing Google-managed configuration (`nam3`) by adding a `READ_ONLY` type replica in location `us-east4`, run:
$ {command} custom-instance-config
--clone-config=nam3
--add-replicas=location=us-east4,type=READ_ONLY
To create a custom Cloud Spanner instance configuration based on another custom configuration (`custom-instance-config`) by adding a `READ_ONLY` type replica in location `us-east1` and removing a `READ_ONLY` type replica in location `us-east4`, run:
$ {command} custom-instance-config1
--clone-config=custom-instance-config
--add-replicas=location=us-east1,type=READ_ONLY
--skip-replicas=location=us-east4,type=READ_ONLY
"""),
}
@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(
'config',
metavar='INSTANCE_CONFIG',
completer=flags.InstanceConfigCompleter,
help='Cloud Spanner instance configuration. The \'custom-\' prefix is required '
'to avoid name conflicts with Google-managed configurations.')
parser.add_argument(
'--display-name',
help='The name of this instance configuration as it appears in UIs. '
'Must specify this option if creating an instance-config with '
'--replicas.')
parser.add_argument(
'--etag', help='Used for optimistic concurrency control.')
base.ASYNC_FLAG.AddToParser(parser)
labels_util.AddCreateLabelsFlags(parser)
parser.add_argument(
'--validate-only',
action='store_true',
default=False,
help='If specified, validate that the creation will succeed without '
'creating the instance configuration.'
)
replica_help_text = """\
The geographic placement of nodes in this instance configuration and
their replication types.
*location*::: The location of the serving resources, e.g. "us-central1".
*type*::: The type of replica.
Items in the list are separated by ":". The allowed values and formats
are as follows.
*READ_ONLY*::::
Read-only replicas only support reads (not writes). Read-only
replicas:
* Maintain a full copy of your data.
* Serve reads.
* Do not participate in voting to commit writes.
* Are not eligible to become a leader.
*READ_WRITE*::::
Read-write replicas support both reads and writes. These
replicas:
* Maintain a full copy of your data.
* Serve reads.
* Can vote whether to commit a write.
* Participate in leadership election.
* Are eligible to become a leader.
*WITNESS*::::
Witness replicas don't support reads but do participate in
voting to commit writes. Witness replicas:
* Do not maintain a full copy of data.
* Do not serve reads.
* Vote whether to commit writes.
* Participate in leader election but are not eligible to become
leader.
"""
clone_or_manual = parser.add_mutually_exclusive_group(required=True)
manual_flags = clone_or_manual.add_argument_group(
'Command-line flags to setup a custom instance configuration replicas:')
flags.ReplicaFlag(manual_flags, name='--replicas', text=replica_help_text)
manual_flags.add_argument(
'--base-config',
required=True,
help='The name of the Google-managed instance configuration, based on which your custom configuration is created.'
)
clone_flags = clone_or_manual.add_argument_group(
'Command-line flags to setup a custom instance configuration using clone options:'
)
clone_flags.add_argument(
'--clone-config',
required=True,
metavar='INSTANCE_CONFIG',
completer=flags.InstanceConfigCompleter,
help='The ID of the instance config, based on which this '
'configuration is created. The clone is an independent copy of this '
'config. Available configurations can be found by running '
'"gcloud spanner instance-configs list"')
flags.ReplicaFlag(
clone_flags,
name='--add-replicas',
text='Add new replicas while cloning from the source config.',
required=False)
flags.ReplicaFlag(
clone_flags,
name='--skip-replicas',
text='Skip replicas from the source config while cloning. Each replica '
'in the list must exist in the source config replicas list.',
required=False)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Instance config create response.
"""
if args.clone_config:
# If the config exists, it's cloned, otherwise, we display the
# error from instanceConfigs.Get.
config = instance_configs.Get(args.clone_config)
op = instance_configs.CreateUsingExistingConfig(args, config)
else:
if not args.IsSpecified('display_name'):
raise c_exceptions.InvalidArgumentException(
'--display-name', 'Must specify --display-name.')
op = instance_configs.CreateUsingReplicas(args.config, args.display_name,
args.base_config, args.replicas,
args.validate_only, args.labels,
args.etag)
# Return immediately when --validate-only is specified. The backend
# implementation returns a fake operation id (0) in case of --validate-only
# flag. Waiting for the operation to complete will result in a NOT_FOUND
# error. As a result, misleading message for users.
if args.async_ or args.validate_only:
return op
return instance_config_operations.Await(op, 'Creating instance-config')

View File

@@ -0,0 +1,82 @@
# -*- 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 for spanner instance configs delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_configs
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.core.console import console_io
class Delete(base.DeleteCommand):
"""Delete a Cloud Spanner instance configuration."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To delete a custom Cloud Spanner instance configuration, run:
$ {command} custom-instance-config
"""),
}
@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(
'--validate-only',
action='store_true',
help='If specified, validate that the deletion will succeed without '
'deleting the instance config.',
)
parser.add_argument(
'--etag',
help='Used for optimistic concurrency control as a way to help prevent '
'simultaneous deletes of an instance config from overwriting each '
'other.')
parser.add_argument(
'config',
metavar='INSTANCE_CONFIG',
completer=flags.InstanceConfigCompleter,
help='Cloud Spanner instance config.')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Instance config delete response, which is empty.
"""
console_io.PromptContinue(
message='Delete instance config [{0}]. Are you sure?'.format(
args.config),
cancel_on_no=True)
return instance_configs.Delete(args.config, args.etag, args.validate_only)

View File

@@ -0,0 +1,71 @@
# -*- 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.
"""Command for spanner instance configs describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_configs
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
class Describe(base.DescribeCommand):
"""Describe a Cloud Spanner instance configuration."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To describe an instance config named regional-us-central1, run:
$ {command} regional-us-central1
To describe an instance config named nam-eur-asia1, run:
$ {command} nam-eur-asia1
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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(
'config',
metavar='INSTANCE_CONFIG',
completer=flags.InstanceConfigCompleter,
help='Cloud Spanner instance config.')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return instance_configs.Get(args.config)

View File

@@ -0,0 +1,60 @@
# -*- 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.
"""Command for spanner instance configs list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_configs
from googlecloudsdk.calliope import base
class List(base.ListCommand):
"""List the available Cloud Spanner instance configurations."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To list the Cloud Spanner instance configs that are availble for this
project, run:
$ {command}
"""),
}
@staticmethod
def Args(parser):
parser.display_info.AddFormat("""
table(
name.basename(),
displayName,
free_instance_availability
)
""")
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return instance_configs.List()

View File

@@ -0,0 +1,103 @@
# -*- 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 for spanner instance configs update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_config_operations
from googlecloudsdk.api_lib.spanner import instance_configs
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.util.args import labels_util
class Update(base.UpdateCommand):
"""Update a Cloud Spanner instance configuration."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To update display name of a custom Cloud Spanner instance configuration 'custom-instance-config', run:
$ {command} custom-instance-config --display-name=nam3-RO-us-central1
To modify the instance config 'custom-instance-config' by adding label 'k0', with value 'value1' and label 'k1' with value 'value2' and removing labels with key 'k3', run:
$ {command} custom-instance-config --update-labels=k0=value1,k1=value2 --remove-labels=k3
To clear all labels of a custom Cloud Spanner instance configuration 'custom-instance-config', run:
$ {command} custom-instance-config --clear-labels
To remove an existing label of a custom Cloud Spanner instance configuration 'custom-instance-config', run:
$ {command} custom-instance-config --remove-labels=KEY1,KEY2
"""),
}
@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(
'config',
metavar='INSTANCE_CONFIG',
completer=flags.InstanceConfigCompleter,
help='Cloud Spanner instance config. The \'custom-\' prefix is required '
'to avoid name conflicts with Google-managed configurations.')
parser.add_argument(
'--display-name',
help='The name of this instance configuration as it appears in UIs.')
parser.add_argument(
'--etag', help='Used for optimistic concurrency control.')
base.ASYNC_FLAG.AddToParser(parser)
labels_util.AddUpdateLabelsFlags(parser)
parser.add_argument(
'--validate-only',
action='store_true',
default=False,
help='Use this flag to validate that the request will succeed before executing it.'
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Instance config update response.
"""
op = instance_configs.Patch(args)
# Return immediately when --validate-only is specified. The backend
# implementation returns a fake operation id (0) in case of --validate-only
# flag. Waiting for the operation to complete will result in a NOT_FOUND
# error. As a result, misleading message for users.
if args.async_ or args.validate_only:
return op
return instance_config_operations.Await(op, 'Updating instance-config')

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for spanner instance partitions."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
class InstancePartitions(base.Group):
"""Manage Spanner instance partitions."""
pass

View File

@@ -0,0 +1,167 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instances partition create."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_partition_operations
from googlecloudsdk.api_lib.spanner import instance_partitions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA
)
class Create(base.CreateCommand):
"""Create a Spanner instance partition."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To create a Spanner instance partition, run:
$ {command} my-instance-partition-id --instance=my-instance-id --config=regional-us-east1 --description=my-instance-display-name --nodes=3
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
resource_args.AddInstancePartitionResourceArg(parser, 'to create')
flags.Config(
text=(
'Instance configuration defines the geographic placement and'
' replication used by the instance partition. Available'
' configurations can be found by running "gcloud spanner'
' instance-configs list"'
)
).AddToParser(parser)
flags.Description(
text='Description of the instance partition.'
).AddToParser(parser)
flags.AddCapacityArgsForInstancePartition(
parser=parser,
add_autoscaling_args=True,
autoscaling_args_hidden=True,
require_all_autoscaling_args=True,
)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.InstancePartitionCompleter)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_partition_ref = args.CONCEPTS.instance_partition.Parse()
instance_ref = instance_partition_ref.Parent()
op = instance_partitions.Create(
instance_ref,
args.instance_partition,
args.config,
args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
)
if args.async_:
return op
instance_partition_operations.Await(op, 'Creating instance partition')
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaCreate(Create):
"""Create a Spanner instance partition with ALPHA features."""
__doc__ = Create.__doc__
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddInstancePartitionResourceArg(parser, 'to create')
flags.Config(
text=(
'Instance configuration defines the geographic placement and'
' replication used by the instance partition. Available'
' configurations can be found by running "gcloud spanner'
' instance-configs list"'
)
).AddToParser(parser)
flags.Description(
text='Description of the instance partition.'
).AddToParser(parser)
flags.AddCapacityArgsForInstancePartition(
parser=parser,
add_autoscaling_args=True,
autoscaling_args_hidden=True,
require_all_autoscaling_args=True,
)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.InstancePartitionCompleter)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_partition_ref = args.CONCEPTS.instance_partition.Parse()
instance_ref = instance_partition_ref.Parent()
op = instance_partitions.Create(
instance_ref,
args.instance_partition,
args.config,
args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
)
if args.async_:
return op
instance_partition_operations.Await(op, 'Creating instance partition')

View File

@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instance partitions delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_partitions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.core.console import console_io
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
class Delete(base.DeleteCommand):
"""Delete a Spanner instance partition. You can't delete the default instance partition using this command.""" # pylint: disable=line-too-long
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To delete a Spanner instance partition, run:
$ {command} my-instance-partition-id --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
resource_args.AddInstancePartitionResourceArg(parser, 'to delete')
parser.display_info.AddCacheUpdater(flags.InstancePartitionCompleter)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_partition_ref = args.CONCEPTS.instance_partition.Parse()
console_io.PromptContinue(
message='Delete instance partition [{0}]. Are you sure?'.format(
args.instance_partition
),
cancel_on_no=True,
)
return instance_partitions.Delete(instance_partition_ref)

View File

@@ -0,0 +1,63 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instance partitions describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_partitions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
class Describe(base.DescribeCommand):
"""Describe a Spanner instance partition."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To describe a Spanner instance partition, run:
$ {command} my-instance-partition-id --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear- pair
for that argument which can follow the argument itself.
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.
"""
resource_args.AddInstancePartitionResourceArg(parser, 'to describe')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return instance_partitions.Get(args.CONCEPTS.instance_partition.Parse())

View File

@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instance partitions list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_partitions
from googlecloudsdk.calliope import base
# from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA, base.ReleaseTrack.ALPHA
)
class List(base.ListCommand):
"""List the Spanner instance partitions contained within the given instance."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To list all Spanner instances partitions in an instance, run:
$ {command} --instance=my-instance-id
"""),
}
@staticmethod
def Args(parser):
resource_args.AddInstanceResourceArg(
parser, 'in which to list instance partitions', positional=False
)
base.FILTER_FLAG.RemoveFromParser(parser) # we don't support filter
parser.display_info.AddFormat("""
table(
name.basename(),
displayName,
config.basename(),
nodeCount,
processing_units,
state
)
""")
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return instance_partitions.List(args.CONCEPTS.instance.Parse())

View File

@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instance partitions update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_partition_operations
from googlecloudsdk.api_lib.spanner import instance_partitions
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(
base.ReleaseTrack.GA, base.ReleaseTrack.BETA
)
class Update(base.Command):
"""Update a Spanner instance partition. You can't update the default instance partition using this command.""" # pylint: disable=line-too-long
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To update the display name of a Spanner instance partition, run:
$ {command} my-instance-partition-id --instance=my-instance-id --description=my-new-display-name
To update the node count of a Spanner instance partition, run:
$ {command} my-instance-partition-id --instance=my-instance-id --nodes=1
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
resource_args.AddInstancePartitionResourceArg(parser, 'to update')
flags.Description(
required=False, text='Description of the instance partition.'
).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
flags.AddCapacityArgsForInstancePartition(
parser=parser,
add_autoscaling_args=True,
autoscaling_args_hidden=True,
require_all_autoscaling_args=False,
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
op = instance_partitions.Patch(
args.CONCEPTS.instance_partition.Parse(),
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
)
if args.async_:
return op
return instance_partition_operations.Await(
op, 'Updating instance partition'
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaUpdate(Update):
"""Update a Spanner instance partition with ALPHA features."""
__doc__ = Update.__doc__
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddInstancePartitionResourceArg(parser, 'to update')
flags.Description(
required=False, text='Description of the instance partition.'
).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
flags.AddCapacityArgsForInstancePartition(
parser=parser,
add_autoscaling_args=True,
autoscaling_args_hidden=True,
require_all_autoscaling_args=False,
)
def Run(self, args):
"""See base class."""
op = instance_partitions.Patch(
args.CONCEPTS.instance_partition.Parse(),
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
)
if args.async_:
return op
return instance_partition_operations.Await(
op, 'Updating instance partition'
)

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""Command group for spanner instances."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Instances(base.Group):
"""Manage Cloud Spanner instances."""
pass

View File

@@ -0,0 +1,34 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Add IAM policy binding to a Cloud Spanner instance.
description: |
Add an IAM policy binding to a Cloud Spanner instance. One binding consists of a member,
a role, and an optional condition.
examples: |
To add an IAM policy binding for the role of 'roles/editor' for the user 'test-user@gmail.com'
with instance 'my-instance', run:
$ {command} my-instance --member='user:test-user@gmail.com' --role='roles/editor'
To add an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/spanner.admin' and the user 'test-user@gmail.com' with instance 'my-instance', run:
$ {command} my-instance --member='user:test-user@gmail.com' --role='roles/spanner.admin' --condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances
arguments:
resource:
help_text: The Cloud Spanner instance to which to add the IAM policy binding.
spec: !REF googlecloudsdk.command_lib.spanner.resources:instance
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

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 Spanner 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 Spanner instance configurations."""

View File

@@ -0,0 +1,38 @@
release_tracks: [ALPHA]
command_type: CONFIG_EXPORT
help_text:
brief: Export the configuration for a Spanner instance.
description: |
*{command}* exports the configuration for a Spanner 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.spanner.resources:instance

View File

@@ -0,0 +1,340 @@
# -*- 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.
"""Command for spanner instances create."""
import textwrap
from googlecloudsdk.api_lib.spanner import instance_operations
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Create(base.CreateCommand):
"""Create a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To create a Cloud Spanner instance, run:
$ {command} my-instance-id --config=regional-us-east1 --description=my-instance-display-name --nodes=3
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
flags.Config().AddToParser(parser)
flags.Description().AddToParser(parser)
flags.Edition(
choices={
'STANDARD': 'Standard edition',
'ENTERPRISE': 'Enterprise edition',
'ENTERPRISE_PLUS': 'Enterprise Plus edition',
'EDITION_UNSPECIFIED': (
"Spanner's legacy pricing model. For more information, see the"
' [Spanner editions overview]'
'(https://cloud.google.com/spanner/docs/editions-overview)'
),
},
).AddToParser(parser)
flags.DefaultBackupScheduleType(
choices={
'DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED': 'Not specified.',
'NONE': (
'No default backup schedule is created automatically when a new'
' database is created in an instance.'
),
'AUTOMATIC': (
'A default backup schedule is created automatically when a new'
' database is created in an instance. You can edit or delete'
" the default backup schedule once it's created. The default"
' backup schedule creates a full backup every 24 hours. These'
' full backups are retained for 7 days.'
),
},
).AddToParser(parser)
resource_args.AddExpireBehaviorArg(parser)
resource_args.AddInstanceTypeArg(parser)
flags.AddCapacityArgsForInstance(
require_all_autoscaling_args=True,
parser=parser,
add_asymmetric_option_flag=True,
autoscaling_cpu_target_group=True,
add_asymmetric_total_cpu_target_flag=True,
add_asymmetric_disable_autoscaling_flags=True,
)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.InstanceCompleter)
flags.AddTags(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_type = resource_args.GetInstanceType(args)
expire_behavior = resource_args.GetExpireBehavior(args)
op = instances.Create(
instance=args.instance,
config=args.config,
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
asymmetric_autoscaling_options=args.asymmetric_autoscaling_option,
instance_type=instance_type,
expire_behavior=expire_behavior,
edition=args.edition,
default_backup_schedule_type=args.default_backup_schedule_type,
tags=args.tags,
)
if args.async_:
return op
instance_operations.Await(op, 'Creating instance')
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaCreate(base.CreateCommand):
"""Create a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To create a Cloud Spanner instance, run:
$ {command} my-instance-id --config=regional-us-east1 --description=my-instance-display-name --nodes=3
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
flags.Config().AddToParser(parser)
flags.Description().AddToParser(parser)
flags.Edition(
choices={
'STANDARD': 'Standard edition',
'ENTERPRISE': 'Enterprise edition',
'ENTERPRISE_PLUS': 'Enterprise Plus edition',
'EDITION_UNSPECIFIED': (
"Spanner's legacy pricing model. For more information, see the"
' [Spanner editions overview]'
'(https://cloud.google.com/spanner/docs/editions-overview)'
),
},
).AddToParser(parser)
flags.DefaultBackupScheduleType(
choices={
'DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED': 'Not specified.',
'NONE': (
'No default backup schedule is created automatically when a new'
' database is created in an instance.'
),
'AUTOMATIC': (
'A default backup schedule is created automatically when a new'
' database is created in an instance. You can edit or delete'
" the default backup schedule once it's created. The default"
' backup schedule creates a full backup every 24 hours. These'
' full backups are retained for 7 days.'
),
},
).AddToParser(parser)
resource_args.AddExpireBehaviorArg(parser)
resource_args.AddInstanceTypeArg(parser)
flags.AddCapacityArgsForInstance(
require_all_autoscaling_args=True,
parser=parser,
add_asymmetric_option_flag=True,
autoscaling_cpu_target_group=True,
add_asymmetric_total_cpu_target_flag=True,
add_asymmetric_disable_autoscaling_flags=True,
add_disable_downscaling_flag=True,
)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.InstanceCompleter)
flags.AddTags(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_type = resource_args.GetInstanceType(args)
expire_behavior = resource_args.GetExpireBehavior(args)
op = instances.Create(
instance=args.instance,
config=args.config,
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
asymmetric_autoscaling_options=args.asymmetric_autoscaling_option,
disable_downscaling=args.disable_downscaling,
instance_type=instance_type,
expire_behavior=expire_behavior,
edition=args.edition,
default_backup_schedule_type=args.default_backup_schedule_type,
tags=args.tags,
)
if args.async_:
return op
instance_operations.Await(op, 'Creating instance')
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaCreate(Create):
"""Create a Cloud Spanner instance with ALPHA features."""
__doc__ = Create.__doc__
@staticmethod
def Args(parser):
"""See base class."""
flags.Instance().AddToParser(parser)
flags.Config().AddToParser(parser)
flags.Description().AddToParser(parser)
flags.SsdCache().AddToParser(parser)
flags.Edition(
choices={
'STANDARD': 'Standard edition',
'ENTERPRISE': 'Enterprise edition',
'ENTERPRISE_PLUS': 'Enterprise Plus edition',
'EDITION_UNSPECIFIED': (
"Spanner's legacy pricing model. For more information, see the"
' [Spanner editions overview]'
'(https://cloud.google.com/spanner/docs/editions-overview)'
),
},
).AddToParser(parser)
flags.DefaultBackupScheduleType(
choices={
'DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED': 'Not specified.',
'NONE': (
'No default backup schedule is created automatically when a new'
' database is created in an instance.'
),
'AUTOMATIC': (
'A default backup schedule is created automatically when a new'
' database is created in an instance. You can edit or delete'
" the default backup schedule once it's created. The default"
' backup schedule creates a full backup every 24 hours. These'
' full backups are retained for 7 days.'
),
},
).AddToParser(parser)
resource_args.AddExpireBehaviorArg(parser)
resource_args.AddInstanceTypeArg(parser)
resource_args.AddDefaultStorageTypeArg(parser)
flags.AddCapacityArgsForInstance(
require_all_autoscaling_args=True,
parser=parser,
add_asymmetric_option_flag=True,
asymmetric_options_group=False,
autoscaling_cpu_target_group=True,
add_asymmetric_total_cpu_target_flag=True,
add_asymmetric_disable_autoscaling_flags=True,
add_disable_downscaling_flag=True,
)
base.ASYNC_FLAG.AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.InstanceCompleter)
flags.AddTags(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_type = resource_args.GetInstanceType(args)
expire_behavior = resource_args.GetExpireBehavior(args)
default_storage_type = resource_args.GetDefaultStorageTypeArg(args)
op = instances.Create(
instance=args.instance,
config=args.config,
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
asymmetric_autoscaling_options=args.asymmetric_autoscaling_option,
disable_downscaling=args.disable_downscaling,
instance_type=instance_type,
expire_behavior=expire_behavior,
default_storage_type=default_storage_type,
ssd_cache=args.ssd_cache,
edition=args.edition,
default_backup_schedule_type=args.default_backup_schedule_type,
tags=args.tags,
)
if args.async_:
return op
instance_operations.Await(op, 'Creating instance')

View File

@@ -0,0 +1,68 @@
# -*- 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.
"""Command for spanner instances delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.core.console import console_io
class Delete(base.DeleteCommand):
"""Delete a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To delete a Cloud Spanner instance, run:
$ {command} my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
parser.display_info.AddCacheUpdater(flags.InstanceCompleter)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
console_io.PromptContinue(
message='Delete instance [{0}]. Are you sure?'.format(args.instance),
cancel_on_no=True)
return instances.Delete(args.instance)

View File

@@ -0,0 +1,63 @@
# -*- 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.
"""Command for spanner instances describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
class Describe(base.DescribeCommand):
"""Describe a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To describe a Cloud Spanner instance, run:
$ {command} my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return instances.Get(args.instance)

View File

@@ -0,0 +1,27 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Get the IAM policy for a Cloud Spanner instance.
description: |
*{command}* displays the IAM policy associated with a Cloud Spanner
instance. If formatted as JSON, the output can be edited and used as
a policy file for *set-iam-policy*. The output includes an "etag"
field identifying the version emitted and allowing detection of
concurrent policy updates; see
$ {parent} set-iam-policy for additional details.
examples: |
To print the IAM policy for a given Cloud Spanner instance, run:
$ {command} my-instance
request:
collection: spanner.projects.instances
arguments:
resource:
help_text: The Cloud Spanner instance for which to display the IAM policy.
spec: !REF googlecloudsdk.command_lib.spanner.resources:instance
iam:
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instances get-locations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
@base.UniverseCompatible
class GetLocations(base.Command):
"""Get the location of every replica in a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To get the location of every replica in a Cloud Spanner instance in this project, run:
$ {command} my-instance-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
For `get-locations` command, we have one positional argument, `instanceId`
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.
"""
flags.Instance().AddToParser(parser)
parser.add_argument(
'--verbose',
required=False,
action='store_true',
help='Indicates that both regions and types of replicas be returned.')
parser.display_info.AddFormat("""table(location:sort=1,type.if(verbose))""")
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. From `Args`, we extract command line
arguments
Returns:
List of dict values for locations of instance
"""
return instances.GetLocations(args.instance, args.verbose)

View File

@@ -0,0 +1,63 @@
# -*- 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.
"""Command for spanner instances list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import base
class List(base.ListCommand):
"""List the Cloud Spanner instances in this project."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To list all Cloud Spanner instances in this project, run:
$ {command}
"""),
}
@staticmethod
def Args(parser):
parser.display_info.AddFormat("""
table(
name.basename(),
displayName,
config.basename(),
nodeCount,
processing_units,
state,
instance_type
)
""")
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return instances.List()

View File

@@ -0,0 +1,206 @@
# -*- 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.
"""Command for spanner instances get-locations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Move(base.Command):
"""Move the Cloud Spanner instance to the specified instance configuration."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To move the Cloud Spanner instance, which has two CMEK-enabled
databases db1 and db2 and a database db3 with Google-managed
encryption keys, to the target instance configuration nam3
(us-east4, us-east1, us-central1), run:
$ gcloud spanner instances move my-instance-id
--target-config=nam3
--target-database-move-configs=^:^database-id=db1:kms-key-names=projects/myproject/locations/us-east4/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-east1/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-central1/keyRings/mykeyring/cryptoKeys/cmek-key
--target-database-move-configs=^:^database-id=db2:kms-key-names=projects/myproject/locations/us-east4/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-east1/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-central1/keyRings/mykeyring/cryptoKeys/cmek-key
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
For `move` command, we have one positional argument, `instanceId`
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.
"""
flags.Instance().AddToParser(parser)
flags.TargetConfig().AddToParser(parser)
parser.add_argument(
'--target-database-move-configs',
metavar='^:^database-id=DATABASE_ID:kms-key-names=KEY1,KEY2',
type=arg_parsers.ArgObject(
spec={
'database-id': str,
'kms-key-names': str,
},
required_keys=['database-id'],
repeated=True,
),
action=arg_parsers.FlattenAction(),
help=(
'Database level configurations for each database to be moved.'
' Currently only used for CMEK-enabled databases to specificy the'
' target database KMS keys.'
),
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. From `Args`, we extract command line
arguments
"""
instances.Move(
args.instance, args.target_config, args.target_database_move_configs
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaMove(base.Command):
"""Move the Cloud Spanner instance to the specified instance configuration."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To move the Cloud Spanner instance, which has two CMEK-enabled
databases db1 and db2 and a database db3 with Google-managed
encryption keys, to the target instance configuration nam3
(us-east4, us-east1, us-central1), run:
$ gcloud beta spanner instances move my-instance-id
--target-config=nam3
--target-database-move-configs=^:^database-id=db1:kms-key-names=projects/myproject/locations/us-east4/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-east1/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-central1/keyRings/mykeyring/cryptoKeys/cmek-key
--target-database-move-configs=^:^database-id=db2:kms-key-names=projects/myproject/locations/us-east4/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-east1/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-central1/keyRings/mykeyring/cryptoKeys/cmek-key
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
For `move` command, we have one positional argument, `instanceId`
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.
"""
flags.Instance().AddToParser(parser)
flags.TargetConfig().AddToParser(parser)
parser.add_argument(
'--target-database-move-configs',
metavar='^:^database-id=DATABASE_ID:kms-key-names=KEY1,KEY2',
type=arg_parsers.ArgObject(
spec={
'database-id': str,
'kms-key-names': str,
},
required_keys=['database-id'],
repeated=True,
),
action=arg_parsers.FlattenAction(),
help=(
'Database level configurations for each database to be moved.'
' Currently only used for CMEK-enabled databases to specificy the'
' target database KMS keys.'
),
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. From `Args`, we extract command line
arguments
"""
instances.Move(
args.instance, args.target_config, args.target_database_move_configs
)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaMove(base.Command):
"""Move the Cloud Spanner instance to the specified instance configuration."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To move the Cloud Spanner instance, which has two CMEK-enabled
databases db1 and db2 and a database db3 with Google-managed
encryption keys, to the target instance configuration nam3
(us-east4, us-east1, us-central1), run:
$ gcloud alpha spanner instances move my-instance-id
--target-config=nam3
--target-database-move-configs=^:^database-id=db1:kms-key-names=projects/myproject/locations/us-east4/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-east1/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-central1/keyRings/mykeyring/cryptoKeys/cmek-key
--target-database-move-configs=^:^database-id=db2:kms-key-names=projects/myproject/locations/us-east4/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-east1/keyRings/mykeyring/cryptoKeys/cmek-key,projects/myproject/locations/us-central1/keyRings/mykeyring/cryptoKeys/cmek-key
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
For `move` command, we have one positional argument, `instanceId`
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.
"""
flags.Instance().AddToParser(parser)
flags.TargetConfig().AddToParser(parser)
parser.add_argument(
'--target-database-move-configs',
metavar='^:^database-id=DATABASE_ID:kms-key-names=KEY1,KEY2',
type=arg_parsers.ArgObject(
spec={
'database-id': str,
'kms-key-names': str,
},
required_keys=['database-id'],
repeated=True,
),
action=arg_parsers.FlattenAction(),
help=(
'Database level configurations for each database to be moved.'
' Currently only used for CMEK-enabled databases to specificy the'
' target database KMS keys.'
),
)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. From `Args`, we extract command line
arguments
"""
instances.Move(
args.instance, args.target_config, args.target_database_move_configs
)

View File

@@ -0,0 +1,33 @@
release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Remove IAM policy binding of a Cloud Spanner instance.
description: |
Remove an IAM policy binding of a Cloud Spanner instance. One binding consists of a member,
a role, and an optional condition.
examples: |
To remove an IAM policy binding for the role of 'roles/editor' for the user 'test-user@gmail.com'
with instance 'my-instance', run:
$ {command} my-instance --member='user:test-user@gmail.com' --role='roles/editor'
To remove an IAM policy binding which expires at the end of the year 2018 for the role of
'roles/spanner.admin' and the user 'test-user@gmail.com' with instance 'my-instance', run:
$ {command} my-instance --member='user:test-user@gmail.com' --role='roles/spanner.admin' --condition='expression=request.time < timestamp("2019-01-01T00:00:00Z"),title=expires_end_of_2018,description=Expires at midnight on 2018-12-31'
See https://cloud.google.com/iam/docs/managing-policies for details of
policy role and member types.
request:
collection: spanner.projects.instances
arguments:
resource:
help_text: The Cloud Spanner instance to remove the IAM policy binding from.
spec: !REF googlecloudsdk.command_lib.spanner.resources:instance
iam:
enable_condition: true
policy_version: 3
get_iam_policy_version_path: getIamPolicyRequest.options.requestedPolicyVersion

View File

@@ -0,0 +1,26 @@
- release_tracks: [ALPHA, BETA, GA]
help_text:
brief: Set the IAM policy for a Cloud Spanner instance.
description: |
Set the IAM policy for a Cloud Spanner instance given a instance ID and
a file encoded in JSON or YAML that contains the IAM policy.
examples: |
The following command reads an IAM policy defined in a JSON file
`policy.json` and sets it for a spanner instance with the ID
`example-instance`:
$ {command} example-instance policy.json
See https://cloud.google.com/iam/docs/managing-policies for details of the
policy file format and contents.
request:
collection: spanner.projects.instances
arguments:
resource:
help_text: The Spanner instance to set the IAM policy for.
spec: !REF googlecloudsdk.command_lib.spanner.resources:instance
iam:
policy_version: 3

View File

@@ -0,0 +1,324 @@
# -*- coding: utf-8 -*- #
# Copyright 2018 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner instances update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import instance_operations
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import resource_args
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Update(base.Command):
"""Update a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To update the display name of a Cloud Spanner instance, run:
$ {command} my-instance-id --description=my-new-display-name
To update the node count of a Cloud Spanner instance, run:
$ {command} my-instance-id --nodes=1
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
flags.Description(required=False).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
resource_args.AddExpireBehaviorArg(parser)
resource_args.AddInstanceTypeArg(parser)
flags.AddCapacityArgsForInstance(
require_all_autoscaling_args=False,
parser=parser,
add_asymmetric_option_flag=True,
asymmetric_options_group=True,
add_asymmetric_total_cpu_target_flag=True,
add_asymmetric_disable_autoscaling_flags=True,
autoscaling_cpu_target_group=True,
)
flags.Edition(None, True).AddToParser(parser)
flags.DefaultBackupScheduleType(
choices={
'DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED': 'Not specified.',
'NONE': (
'No default backup schedule is created automatically when a new'
' database is created in an instance.'
),
'AUTOMATIC': (
'A default backup schedule is created automatically when a new'
' database is created in an instance. You can edit or delete'
" the default backup schedule once it's created. The default"
' backup schedule creates a full backup every 24 hours. These'
' full backups are retained for 7 days.'
),
},
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_type = resource_args.GetInstanceType(args)
expire_behavior = resource_args.GetExpireBehavior(args)
op = instances.Patch(
args.instance,
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
asymmetric_autoscaling_options=args.asymmetric_autoscaling_option,
clear_asymmetric_autoscaling_options=args.clear_asymmetric_autoscaling_option,
instance_type=instance_type,
expire_behavior=expire_behavior,
edition=args.edition,
default_backup_schedule_type=args.default_backup_schedule_type,
)
if args.async_:
return op
instance_operations.Await(op, 'Updating instance')
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaUpdate(base.Command):
"""Update a Cloud Spanner instance."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To update the display name of a Cloud Spanner instance, run:
$ {command} my-instance-id --description=my-new-display-name
To update the node count of a Cloud Spanner instance, run:
$ {command} my-instance-id --nodes=1
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
flags.Description(required=False).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
resource_args.AddExpireBehaviorArg(parser)
resource_args.AddInstanceTypeArg(parser)
flags.AddCapacityArgsForInstance(
require_all_autoscaling_args=False,
parser=parser,
add_asymmetric_option_flag=True,
asymmetric_options_group=True,
add_asymmetric_total_cpu_target_flag=True,
add_asymmetric_disable_autoscaling_flags=True,
autoscaling_cpu_target_group=True,
add_disable_downscaling_flag=True,
)
flags.Edition(None, True).AddToParser(parser)
flags.DefaultBackupScheduleType(
choices={
'DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED': 'Not specified.',
'NONE': (
'No default backup schedule is created automatically when a new'
' database is created in an instance.'
),
'AUTOMATIC': (
'A default backup schedule is created automatically when a new'
' database is created in an instance. You can edit or delete'
" the default backup schedule once it's created. The default"
' backup schedule creates a full backup every 24 hours. These'
' full backups are retained for 7 days.'
),
},
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_type = resource_args.GetInstanceType(args)
expire_behavior = resource_args.GetExpireBehavior(args)
op = instances.Patch(
args.instance,
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
asymmetric_autoscaling_options=args.asymmetric_autoscaling_option,
disable_downscaling=args.disable_downscaling,
clear_asymmetric_autoscaling_options=args.clear_asymmetric_autoscaling_option,
instance_type=instance_type,
expire_behavior=expire_behavior,
edition=args.edition,
default_backup_schedule_type=args.default_backup_schedule_type,
)
if args.async_:
return op
instance_operations.Await(op, 'Updating instance')
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaUpdate(base.Command):
"""Update a Cloud Spanner instance with ALPHA features."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To update the display name of a Cloud Spanner instance, run:
$ {command} my-instance-id --description=my-new-display-name
To update the node count of a Cloud Spanner instance, run:
$ {command} my-instance-id --nodes=1
"""),
}
__doc__ = Update.__doc__
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.Instance().AddToParser(parser)
flags.Description(required=False).AddToParser(parser)
base.ASYNC_FLAG.AddToParser(parser)
resource_args.AddExpireBehaviorArg(parser)
resource_args.AddInstanceTypeArg(parser)
flags.AddCapacityArgsForInstance(
require_all_autoscaling_args=False,
parser=parser,
add_asymmetric_option_flag=True,
asymmetric_options_group=True,
autoscaling_cpu_target_group=True,
add_asymmetric_total_cpu_target_flag=True,
add_asymmetric_disable_autoscaling_flags=True,
add_disable_downscaling_flag=True,
)
flags.SsdCache().AddToParser(parser)
flags.Edition(None, True).AddToParser(parser)
flags.DefaultBackupScheduleType(
choices={
'DEFAULT_BACKUP_SCHEDULE_TYPE_UNSPECIFIED': 'Not specified.',
'NONE': (
'No default backup schedule is created automatically when a new'
' database is created in an instance.'
),
'AUTOMATIC': (
'A default backup schedule is created automatically when a new'
' database is created in an instance. You can edit or delete'
" the default backup schedule once it's created. The default"
' backup schedule creates a full backup every 24 hours. These'
' full backups are retained for 7 days.'
),
},
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
instance_type = resource_args.GetInstanceType(args)
expire_behavior = resource_args.GetExpireBehavior(args)
op = instances.Patch(
args.instance,
description=args.description,
nodes=args.nodes,
processing_units=args.processing_units,
autoscaling_min_nodes=args.autoscaling_min_nodes,
autoscaling_max_nodes=args.autoscaling_max_nodes,
autoscaling_min_processing_units=args.autoscaling_min_processing_units,
autoscaling_max_processing_units=args.autoscaling_max_processing_units,
autoscaling_high_priority_cpu_target=args.autoscaling_high_priority_cpu_target,
autoscaling_total_cpu_target=args.autoscaling_total_cpu_target,
autoscaling_storage_target=args.autoscaling_storage_target,
asymmetric_autoscaling_options=args.asymmetric_autoscaling_option,
disable_downscaling=args.disable_downscaling,
clear_asymmetric_autoscaling_options=args.clear_asymmetric_autoscaling_option,
instance_type=instance_type,
expire_behavior=expire_behavior,
ssd_cache_id=args.ssd_cache,
edition=args.edition,
default_backup_schedule_type=args.default_backup_schedule_type,
)
if args.async_:
return op
instance_operations.Await(op, 'Updating instance')

View File

@@ -0,0 +1,26 @@
# -*- 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.
"""The migrate command group for spanner."""
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 Migration(base.Group):
"""Migrate external databases to Cloud Spanner."""

View File

@@ -0,0 +1,76 @@
# -*- 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.
"""Cleanup migration resources given a data migration job id."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import migration_backend
class Cleanup(base.BinaryBackedCommand):
"""Cleanup migration resources given a data migration job id."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To cleanup resources for a data migration job, specify the jobId and the
boolean flags for the resources that need to be cleaned up. For sharded
migrations, specific data shard Ids can also be provided.
For a all shards of a sharded migration, or a non-sharded migration:
$ {command} --job-id="XXX" --target-profile="XXX" --datastream --dataflow --pub-sub --monitoring
For a subset of shards of a sharded migration:
$ {command} --job-id="XXX" --data-shard-ids="lorem,epsum" --target-profile="XXX" --datastream --dataflow --pub-sub --monitoring
"""),
}
@staticmethod
def Args(parser):
"""Register the flags for this command."""
flags.GetSpannerMigrationJobIdFlag().AddToParser(parser)
flags.GetSpannerMigrationDataShardIdsFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationCleanupDatastreamResourceFlag().AddToParser(parser)
flags.GetSpannerMigrationCleanupDataflowResourceFlag().AddToParser(parser)
flags.GetSpannerMigrationCleanupPubsubResourceFlag().AddToParser(parser)
flags.GetSpannerMigrationCleanupMonitoringResourceFlag().AddToParser(parser)
flags.GetSpannerMigrationLogLevelFlag().AddToParser(parser)
def Run(self, args):
"""Run the schema-and-data command."""
command_executor = migration_backend.SpannerMigrationWrapper()
env_vars = migration_backend.GetEnvArgsForCommand(
extra_vars={'GCLOUD_HB_PLUGIN': 'true'}
)
response = command_executor(
command='cleanup',
job_id=args.job_id,
data_shard_ids=args.data_shard_ids,
target_profile=args.target_profile,
datastream=args.datastream,
dataflow=args.dataflow,
pub_sub=args.pub_sub,
monitoring=args.monitoring,
log_level=args.log_level,
env=env_vars,
)
self.exit_code = response.exit_code
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,78 @@
# -*- 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.
"""Migrate data from a source database to Cloud Spanner given a schema."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import migration_backend
@base.DefaultUniverseOnly
class Data(base.BinaryBackedCommand):
"""Migrate data from a source database to Cloud Spanner given a schema."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To copy data to Cloud Spanner given a session file:
$ {command} --session=./session.json --source=postgresql < ~/cart.pg_dump --target-profile='instance=spanner-instance'
"""),
}
@staticmethod
def Args(parser):
"""Register the flags for this command."""
flags.GetSpannerMigrationSourceFlag().AddToParser(parser)
flags.GetSpannerMigrationSessionFlag().AddToParser(parser)
flags.GetSpannerMigrationPrefixFlag().AddToParser(parser)
flags.GetSpannerMigrationSkipForeignKeysFlag().AddToParser(parser)
flags.GetSpannerMigrationSourceProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationWriteLimitFlag().AddToParser(parser)
flags.GetSpannerMigrationDryRunFlag().AddToParser(parser)
flags.GetSpannerMigrationLogLevelFlag().AddToParser(parser)
flags.GetSpannerMigrationProjectFlag().AddToParser(parser)
flags.GetSpannerMigrationDataflowTemplateFlag().AddToParser(parser)
def Run(self, args):
"""Run the data command."""
command_executor = migration_backend.SpannerMigrationWrapper()
env_vars = migration_backend.GetEnvArgsForCommand(
extra_vars={'GCLOUD_HB_PLUGIN': 'true'})
response = command_executor(
command='data',
source=args.source,
session=args.session,
prefix=args.prefix,
skip_foreign_keys=args.skip_foreign_keys,
source_profile=args.source_profile,
target=args.target,
target_profile=args.target_profile,
write_limit=args.write_limit,
dry_run=args.dry_run,
log_level=args.log_level,
project=args.project,
dataflow_template=args.dataflow_template,
env=env_vars,
)
self.exit_code = response.exit_code
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,70 @@
# -*- 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.
"""Migrate schema from a source database to Cloud Spanner."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import migration_backend
@base.DefaultUniverseOnly
class Schema(base.BinaryBackedCommand):
"""Migrate schema from a source database to Cloud Spanner."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To generate schema file from the source database:
$ {command} --source=postgresql < ~/cart.pg_dump
"""),
}
@staticmethod
def Args(parser):
"""Register the flags for this command."""
flags.GetSpannerMigrationSourceFlag().AddToParser(parser)
flags.GetSpannerMigrationPrefixFlag().AddToParser(parser)
flags.GetSpannerMigrationSourceProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationDryRunFlag().AddToParser(parser)
flags.GetSpannerMigrationLogLevelFlag().AddToParser(parser)
flags.GetSpannerMigrationProjectFlag().AddToParser(parser)
def Run(self, args):
"""Run the schema command."""
command_executor = migration_backend.SpannerMigrationWrapper()
env_vars = migration_backend.GetEnvArgsForCommand(
extra_vars={'GCLOUD_HB_PLUGIN': 'true'})
response = command_executor(
command='schema',
source=args.source,
prefix=args.prefix,
source_profile=args.source_profile,
target=args.target,
target_profile=args.target_profile,
dry_run=args.dry_run,
log_level=args.log_level,
project=args.project,
env=env_vars,
)
self.exit_code = response.exit_code
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,76 @@
# -*- 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.
"""Migrate data from a source database to Cloud Spanner given a schema."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import migration_backend
@base.DefaultUniverseOnly
class SchemaAndData(base.BinaryBackedCommand):
"""Migrate data from a source database to Cloud Spanner given a schema."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To generate schema and copy data to Cloud Spanner from a source database:
$ {command} --source=postgresql < ~/cart.pg_dump --target-profile='instance=spanner-instance'
"""),
}
@staticmethod
def Args(parser):
"""Register the flags for this command."""
flags.GetSpannerMigrationSourceFlag().AddToParser(parser)
flags.GetSpannerMigrationPrefixFlag().AddToParser(parser)
flags.GetSpannerMigrationSkipForeignKeysFlag().AddToParser(parser)
flags.GetSpannerMigrationSourceProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetFlag().AddToParser(parser)
flags.GetSpannerMigrationTargetProfileFlag().AddToParser(parser)
flags.GetSpannerMigrationWriteLimitFlag().AddToParser(parser)
flags.GetSpannerMigrationDryRunFlag().AddToParser(parser)
flags.GetSpannerMigrationLogLevelFlag().AddToParser(parser)
flags.GetSpannerMigrationProjectFlag().AddToParser(parser)
flags.GetSpannerMigrationDataflowTemplateFlag().AddToParser(parser)
def Run(self, args):
"""Run the schema-and-data command."""
command_executor = migration_backend.SpannerMigrationWrapper()
env_vars = migration_backend.GetEnvArgsForCommand(
extra_vars={'GCLOUD_HB_PLUGIN': 'true'})
response = command_executor(
command='schema-and-data',
source=args.source,
prefix=args.prefix,
skip_foreign_keys=args.skip_foreign_keys,
source_profile=args.source_profile,
target=args.target,
target_profile=args.target_profile,
write_limit=args.write_limit,
dry_run=args.dry_run,
log_level=args.log_level,
project=args.project,
dataflow_template=args.dataflow_template,
env=env_vars,
)
self.exit_code = response.exit_code
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,62 @@
# -*- 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.
"""Run the web UI assistant for schema migrations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
from googlecloudsdk.command_lib.spanner import migration_backend
@base.DefaultUniverseOnly
class Web(base.BinaryBackedCommand):
"""Run the web UI assistant for schema migrations."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To run the web UI assistant:
$ {command}
"""),
}
@staticmethod
def Args(parser):
"""Register the flags for this command."""
flags.GetSpannerMigrationWebPortFlag().AddToParser(parser)
flags.GetSpannerMigrationWebOpenFlag().AddToParser(parser)
flags.GetSpannerMigrationLogLevelFlag().AddToParser(parser)
flags.GetSpannerMigrationDataflowTemplateFlag().AddToParser(parser)
def Run(self, args):
"""Run the web UI assistant."""
command_executor = migration_backend.SpannerMigrationWrapper()
env_vars = migration_backend.GetEnvArgsForCommand(
extra_vars={'GCLOUD_HB_PLUGIN': 'true'})
response = command_executor(
command='web',
open_flag=args.open,
port=args.port,
log_level=args.log_level,
dataflow_template=args.dataflow_template,
env=env_vars,
)
self.exit_code = response.exit_code
return self._DefaultOperationResponseHandler(response)

View File

@@ -0,0 +1,27 @@
# -*- 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.
"""Command group for spanner operations."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.DefaultUniverseOnly
class Operations(base.Group):
"""Manage Cloud Spanner operations."""
pass

View File

@@ -0,0 +1,138 @@
# -*- 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.
"""Command for spanner operations cancel."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_operations
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import instance_config_operations
from googlecloudsdk.api_lib.spanner import instance_operations
from googlecloudsdk.api_lib.spanner import instance_partition_operations
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
DETAILED_HELP = {
'EXAMPLES': textwrap.dedent("""\
To cancel an instance operation with ID _auto_12345, run:
$ {command} _auto_12345 --instance=my-instance-id
To cancel a database operation with ID _auto_12345, run:
$ {command} _auto_12345 --instance=my-instance-id
--database=my-database-id
To cancel a backup operation with ID _auto_12345, run:
$ {command} _auto_12345 --instance=my-instance-id
--backup=my-backup-id
To cancel an instance partition operation with ID auto_12345, run:
$ {command} auto_12345 --instance=my-instance-id --instance-partition=my-partition-id
"""),
}
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Cancel(base.Command):
"""Cancel a Cloud Spanner operation."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
mutex_group = parser.add_group(mutex=True, required=True)
mutex_group.add_argument(
'--instance-config',
completer=flags.InstanceConfigCompleter,
help='The ID of the instance configuration the operation is executing on.'
)
mutex_group.add_argument(
'--instance',
completer=flags.InstanceCompleter,
help='The ID of the instance the operation is executing on.')
flags.AddCommonCancelArgs(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
if args.instance_config:
return instance_config_operations.Cancel(args.instance_config,
args.operation)
# Checks that user only specified database or backup or instance partition
# flag.
flags.CheckExclusiveLROFlagsUnderInstance(args)
if args.backup:
return backup_operations.Cancel(args.instance, args.backup,
args.operation)
if args.database:
return database_operations.Cancel(args.instance, args.database,
args.operation)
if args.instance_partition:
return instance_partition_operations.Cancel(
args.instance, args.instance_partition, args.operation
)
return instance_operations.Cancel(args.instance, args.operation)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA)
class BetaAndAlphaCancel(Cancel):
"""Cancel a Cloud Spanner operation."""
detailed_help = {
'EXAMPLES': DETAILED_HELP['EXAMPLES'] + textwrap.dedent("""\
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
super(BetaAndAlphaCancel, BetaAndAlphaCancel).Args(parser)

View File

@@ -0,0 +1,186 @@
# -*- 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.
"""Command for spanner operations describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_operations
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import instance_config_operations
from googlecloudsdk.api_lib.spanner import instance_operations
from googlecloudsdk.api_lib.spanner import instance_partition_operations
from googlecloudsdk.api_lib.spanner import ssd_cache_operations
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exceptions
from googlecloudsdk.command_lib.spanner import flags
DETAILED_HELP = {
'EXAMPLES': textwrap.dedent("""\
To describe a Cloud Spanner instance operation, run:
$ {command} _auto_12345 --instance=my-instance-id
To describe a Cloud Spanner database operation, run:
$ {command} _auto_12345 --instance=my-instance-id
--database=my-database-id
To describe a Cloud Spanner backup operation, run:
$ {command} _auto_12345 --instance=my-instance-id
--backup=my-backup-id
To describe an instance partition operation, run:
$ {command} _auto_12345 --instance=my-instance-id --instance-partition=my-partition-id
"""),
}
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class Describe(base.DescribeCommand):
"""Describe a Cloud Spanner operation."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
mutex_group = parser.add_group(mutex=True, required=True)
mutex_group.add_argument(
'--instance-config',
completer=flags.InstanceConfigCompleter,
help='The ID of the instance configuration the operation is executing on.'
)
mutex_group.add_argument(
'--instance',
completer=flags.InstanceCompleter,
help='The ID of the instance the operation is executing on.')
flags.AddCommonDescribeArgs(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
if args.instance_config:
return instance_config_operations.Get(args.instance_config,
args.operation)
# Checks that user only specified database or backup or instance partition
# flag.
flags.CheckExclusiveLROFlagsUnderInstance(args)
if args.backup:
return backup_operations.Get(args.instance, args.backup, args.operation)
if args.database:
return database_operations.Get(args.instance, args.database,
args.operation)
if args.instance_partition:
return instance_partition_operations.Get(
args.instance, args.instance_partition, args.operation
)
return instance_operations.Get(args.instance, args.operation)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaDescribe(Describe):
"""Describe a Cloud Spanner operation."""
detailed_help = {
'EXAMPLES': DETAILED_HELP['EXAMPLES'] + textwrap.dedent("""\
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
super(BetaDescribe, BetaDescribe).Args(parser)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaDescribe(BetaDescribe):
"""Describe a Cloud Spanner operation."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
super(AlphaDescribe, AlphaDescribe).Args(parser)
flags.SsdCache(
positional=False,
required=False,
hidden=True,
text='The ID of the SSD Cache the operation is executing on.',
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
if args.ssd_cache:
if args.instance:
raise c_exceptions.InvalidArgumentException(
'--instance or --ssd-cache',
'The `--instance` flag cannot be used with `--ssd-cache`.',
)
return ssd_cache_operations.Get(
args.operation, args.ssd_cache, args.instance_config
)
return super().Run(args)

View File

@@ -0,0 +1,306 @@
# -*- 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.
"""Command for spanner operations list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import backup_operations
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import instance_config_operations
from googlecloudsdk.api_lib.spanner import instance_operations
from googlecloudsdk.api_lib.spanner import instance_partition_operations
from googlecloudsdk.api_lib.spanner import ssd_cache_operations
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as c_exceptions
from googlecloudsdk.command_lib.spanner import flags
DETAILED_HELP = {
'EXAMPLES': textwrap.dedent("""\
To list Cloud Spanner instance operations for an instance, run:
$ {command} --instance=my-instance-id --type=INSTANCE
To list Cloud Spanner backup operations for an instance, run:
$ {command} --instance=my-instance-id --type=BACKUP
To list Cloud Spanner database operations for an instance, run:
$ {command} --instance=my-instance-id --type=DATABASE
To list Cloud Spanner database operations for a database, run:
$ {command} --instance=my-instance-id --database=my-database-id --type=DATABASE
To list Cloud Spanner backup operations for a database, run:
$ {command} --instance=my-instance-id --database=my-database-id --type=BACKUP
To list Cloud Spanner backup operations for a backup, run:
$ {command} --instance=my-instance-id --backup=my-backup-id --type=BACKUP
To list instance partition operations for an instance partition, run:
$ {command} --instance=my-instance-id --instance-partition=my-partition-id --type=INSTANCE_PARTITION
To list instance partition operations for all instance partitions belonging to this instance, run:
$ {command} --instance=my-instance-id --type=INSTANCE_PARTITION
"""),
}
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.GA)
class List(base.ListCommand):
"""List the Cloud Spanner operations."""
detailed_help = DETAILED_HELP
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.AddCommonListArgs(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
if args.instance_config:
type_filter = (
instance_config_operations.BuildInstanceConfigOperationTypeFilter(
args.type))
return instance_config_operations.List(args.instance_config, type_filter)
is_database_type = (
args.type == 'DATABASE_RESTORE'
or args.type == 'DATABASE'
or args.type == 'DATABASE_CREATE'
or args.type == 'DATABASE_UPDATE_DDL'
or args.type == 'DATABASE_CHANGE_QUORUM'
)
if args.backup or args.type == 'BACKUP':
# Update output table for backup operations.
# pylint:disable=protected-access
args._GetParser().ai.display_info.AddFormat("""
table(
name.basename():label=OPERATION_ID,
done():label=DONE,
metadata.'@type'.split('.').slice(-1:).join(),
metadata.name.split('/').slice(-1:).join():label=BACKUP,
metadata.database.split('/').slice(-1).join():label=SOURCE_DATABASE,
metadata.progress.startTime:label=START_TIME,
metadata.progress.endTime:label=END_TIME
)
""")
if args.type == 'DATABASE_RESTORE':
# Update output table for restore operations.
# pylint:disable=protected-access
args._GetParser().ai.display_info.AddFormat("""
table(
name.basename():label=OPERATION_ID,
done():label=DONE,
metadata.'@type'.split('.').slice(-1:).join(),
metadata.name.split('/').slice(-1:).join():label=RESTORED_DATABASE,
metadata.backupInfo.backup.split('/').slice(-1).join():label=SOURCE_BACKUP,
metadata.progress.startTime:label=START_TIME,
endtime():label=END_TIME
)
""")
elif is_database_type:
# Update output table for database operations.
# pylint:disable=protected-access
args._GetParser().ai.display_info.AddFormat("""
table(
name.basename():label=OPERATION_ID,
metadata.statements.join(sep="\n"),
done():label=DONE,
metadata.'@type'.split('.').slice(-1:).join(),
database().split('/').slice(-1:).join():label=DATABASE_ID
)
""")
# Checks that user only specified database or backup or instance partition
# flag.
flags.CheckExclusiveLROFlagsUnderInstance(args)
# Checks that the user did not specify the backup flag with the type filter
# set to a database operation type.
if (args.IsSpecified('backup') and is_database_type):
raise c_exceptions.InvalidArgumentException(
'--backup or --type',
'The backup flag cannot be used with the type flag set to a '
'database operation type.')
if args.type == 'INSTANCE':
if args.IsSpecified('database'):
raise c_exceptions.InvalidArgumentException(
'--database or --type',
'The `--database` flag cannot be used with `--type=INSTANCE`.')
if args.IsSpecified('backup'):
raise c_exceptions.InvalidArgumentException(
'--backup or --type',
'The `--backup` flag cannot be used with `--type=INSTANCE`.')
if args.IsSpecified('instance_partition'):
raise c_exceptions.InvalidArgumentException(
'--instance-partition or --type',
'The `--instance-partition` flag cannot be used with'
' `--type=INSTANCE`.',
)
if args.type == 'BACKUP':
if args.database:
db_filter = backup_operations.BuildDatabaseFilter(
args.instance, args.database)
return backup_operations.List(args.instance, db_filter)
if args.backup:
return backup_operations.ListGeneric(args.instance, args.backup)
return backup_operations.List(args.instance)
if is_database_type:
type_filter = database_operations.BuildDatabaseOperationTypeFilter(
args.type)
return database_operations.ListDatabaseOperations(args.instance,
args.database,
type_filter)
if args.backup:
return backup_operations.ListGeneric(args.instance, args.backup)
if args.database:
return database_operations.List(args.instance, args.database)
if args.type == 'INSTANCE_PARTITION':
# Update output table for instance partition operations.
# pylint:disable=protected-access
args.GetDisplayInfo().AddFormat("""
table(
name.basename():label=OPERATION_ID,
done():label=DONE,
metadata.'@type'.split('.').slice(-1:).join(),
metadata.instancePartition.name.split('/').slice(-1:).join():label=INSTANCE_PARTITION_ID,
metadata.startTime:label=START_TIME,
metadata.endTime:label=END_TIME
)
""")
if args.instance_partition:
return instance_partition_operations.ListGeneric(
args.instance, args.instance_partition
)
else:
return instance_partition_operations.List(args.instance)
return instance_operations.List(args.instance)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.BETA)
class BetaList(List):
"""List the Cloud Spanner operations."""
detailed_help = {
'EXAMPLES': DETAILED_HELP['EXAMPLES'] + textwrap.dedent("""\
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
super(BetaList, BetaList).Args(parser)
@base.DefaultUniverseOnly
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
class AlphaList(BetaList):
"""List the Cloud Spanner operations."""
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
super(AlphaList, AlphaList).Args(parser)
flags.SsdCache(
positional=False,
required=False,
hidden=True,
text='For SSD Cache operations, the SSD Cache ID.',
).AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
if args.ssd_cache:
if args.instance:
raise c_exceptions.InvalidArgumentException(
'--instance or --ssd-cache',
'The `--instance` flag cannot be used with `--ssd-cache`.',
)
if args.type:
raise c_exceptions.InvalidArgumentException(
'--type or --ssd-cache',
'The `--type` flag cannot be used with `--ssd-cache`.',
)
# Update output table for SSD Cache operations.
# pylint:disable=protected-access
args._GetParser().ai.display_info.AddFormat("""
table(
name.basename():label=OPERATION_ID,
done():label=DONE,
metadata.'@type'.split('.').slice(-1:).join(),
metadata.startTime:label=START_TIME,
metadata.endTime:label=END_TIME
)
""")
return ssd_cache_operations.List(args.ssd_cache, args.instance_config)
return super().Run(args)

View File

@@ -0,0 +1,28 @@
# -*- 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.
"""Command group for spanner rows."""
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 Rows(base.Group):
"""Manage the rows in Cloud Spanner databases."""
pass

View File

@@ -0,0 +1,87 @@
# -*- 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.
"""Command for spanner rows delete."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.command_lib.spanner import write_util
from googlecloudsdk.core import resources
class Delete(base.DeleteCommand):
# pylint:disable=line-too-long
"""Delete a row in a Cloud Spanner database.
## EXAMPLES
To delete a row with primary keys of SingerId=1,SingName=abc in table Singers
under my-database and my-instance, run:
$ {command} --table=Singers --database=my-database --instance=my-instance --keys=1,abc
"""
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'in which to delete a row',
False)
parser.add_argument(
'--table',
required=True,
type=str,
help='The Cloud Spanner table name.')
parser.add_argument(
'--keys',
required=True,
metavar='KEY',
type=arg_parsers.ArgList(),
help='The primary key values of the row to delete.')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
database_ref = args.CONCEPTS.database.Parse()
# DDL(Data Definition Language) is needed to get the schema of the current
# database and table so that we know the type of each column (e.g. INT64)
# user wants to delete.
ddl = databases.GetDdl(database_ref)
table = write_util.Table.FromDdl(ddl, args.table)
mutation = database_sessions.MutationFactory.Delete(table, args.keys)
# To commit a transaction in a session, we need to create one and delete it
# at the end.
session_name = database_sessions.Create(database_ref)
session = resources.REGISTRY.ParseRelativeName(
relative_name=session_name.name,
collection='spanner.projects.instances.databases.sessions')
try:
return database_sessions.Commit(session, [mutation])
finally:
database_sessions.Delete(session)

View File

@@ -0,0 +1,86 @@
# -*- 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.
"""Command for spanner rows insert."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.command_lib.spanner import write_util
from googlecloudsdk.core import resources
class Insert(base.Command):
# pylint:disable=line-too-long
"""Insert a row in a Cloud Spanner database.
## EXAMPLES
To insert a row with SingerId=1,SingName=abc in table Singers under
my-database and my-instance, run:
$ {command} --table=Singers --database=my-database --instance=my-instance --data=SingerId=1,SingerName=abc
$ {command} --table=Singers --database=my-database --instance=my-instance --flags-file=path/to/file.yaml
"""
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'in which to insert a row',
False)
parser.add_argument(
'--table',
required=True,
type=str,
help='The Cloud Spanner table name.')
parser.add_argument(
'--data',
required=True,
metavar='COLUMN_NAME=VALUE',
type=arg_parsers.ArgDict(),
help='The column names and values for the row being added. '
'For complicated input values, such as arrays, use the `--flags-file` '
'flag. See $ gcloud topic flags-file for more information.')
def Run(self, args):
"""This is what gets called when the user runs this command."""
database_ref = args.CONCEPTS.database.Parse()
# DDL(Data Definition Language) is needed to get the schema of the current
# database and table so that we know the type of each column (e.g. INT64)
# user wants to delete.
ddl = databases.GetDdl(database_ref)
table = write_util.Table.FromDdl(ddl, args.table)
data = write_util.ValidateArrayInput(table, args.data)
mutation = database_sessions.MutationFactory.Insert(table, data)
# To commit a transaction in a session, we need to create one and delete it
# at the end.
session_name = database_sessions.Create(database_ref)
session = resources.REGISTRY.ParseRelativeName(
relative_name=session_name.name,
collection='spanner.projects.instances.databases.sessions')
try:
return database_sessions.Commit(session, [mutation])
finally:
database_sessions.Delete(session)

View File

@@ -0,0 +1,86 @@
# -*- 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.
"""Command for spanner rows update."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import resource_args
from googlecloudsdk.command_lib.spanner import write_util
from googlecloudsdk.core import resources
class Update(base.Command):
# pylint:disable=line-too-long
"""Update a row in a Cloud Spanner database.
## EXAMPLES
To update a row with SingerId=1,SingName=abc in table Singers under
my-database and my-instance, run:
$ {command} --table=Singers --database=my-database --instance=my-instance --data=SingerId=1,SingerName=abc
$ {command} --table=Singers --database=my-database --instance=my-instance --flags-file=path/to/file.yaml
"""
@staticmethod
def Args(parser):
"""See base class."""
resource_args.AddDatabaseResourceArg(parser, 'in which to update a row',
False)
parser.add_argument(
'--table',
required=True,
type=str,
help='The Cloud Spanner table name.')
parser.add_argument(
'--data',
required=True,
metavar='COLUMN_NAME=VALUE',
type=arg_parsers.ArgDict(),
help='The column names and values for the row being updated. '
'For complicated input values, such as arrays, use the `--flags-file` '
'flag. See $ gcloud topic flags-file for more information.')
def Run(self, args):
"""This is what gets called when the user runs this command."""
database_ref = args.CONCEPTS.database.Parse()
# DDL(Data Definition Language) is needed to get the schema of the current
# database and table so that we know the type of each column (e.g. INT64)
# user wants to delete.
ddl = databases.GetDdl(database_ref)
table = write_util.Table.FromDdl(ddl, args.table)
data = write_util.ValidateArrayInput(table, args.data)
mutation = database_sessions.MutationFactory.Update(table, data)
# To commit a transaction in a session, we need to create one and delete it
# at the end.
session_name = database_sessions.Create(database_ref)
session = resources.REGISTRY.ParseRelativeName(
relative_name=session_name.name,
collection='spanner.projects.instances.databases.sessions')
try:
return database_sessions.Commit(session, [mutation])
finally:
database_sessions.Delete(session)

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command group for spanner samples."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
class Samples(base.Group):
"""Cloud Spanner sample apps.
Each Cloud Spanner sample application includes a backend gRPC service
backed by a Cloud Spanner database and a workload script that generates
service traffic.
These sample apps are open source and available at
https://github.com/GoogleCloudPlatform/cloud-spanner-samples.
To see a list of available sample apps, run:
$ gcloud spanner samples list
"""
pass

View File

@@ -0,0 +1,208 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner samples backend."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
import textwrap
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.spanner import samples
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from surface.spanner.samples import init as samples_init
def _get_logfile_name(appname):
return '{}-backend.log'.format(appname)
def _get_popen_jar(appname):
if appname not in samples.APPS:
raise ValueError("Unknown sample app '{}'".format(appname))
return os.path.join(
samples.get_local_bin_path(appname), samples.APPS[appname].backend_bin)
# Note: Currently all apps supported use the same flag definitions.
# If there is a need for different flags by app in the future this logic can
# move to: third_party/py/googlecloudsdk/command_lib/spanner/samples.py
def _get_popen_args(project, appname, instance_id, database_id=None, port=None):
"""Get formatted args for server command."""
if database_id is None:
database_id = samples.get_db_id_for_app(appname)
flags = [
'--spanner_project_id={}'.format(project),
'--spanner_instance_id={}'.format(instance_id),
'--spanner_database_id={}'.format(database_id)
]
if port is not None:
flags.append('--port={}'.format(port))
if samples.get_database_dialect(
appname) == databases.DATABASE_DIALECT_POSTGRESQL:
flags.append('--spanner_use_pg')
return flags
def run_backend(project,
appname,
instance_id,
database_id=None,
port=None,
capture_logs=False):
"""Run the backend service executable for the given sample app.
Args:
project: str, Name of the GCP project.
appname: str, Name of the sample app.
instance_id: str, Cloud Spanner instance ID.
database_id: str, Cloud Spanner database ID.
port: int, Port to run the service on.
capture_logs: bool, Whether to save logs to disk or print to stdout.
Returns:
subprocess.Popen or execution_utils.SubprocessTimeoutWrapper, The running
subprocess.
"""
proc_args = ['java', '-jar']
proc_args.append(_get_popen_jar(appname))
proc_args.extend(
_get_popen_args(project, appname, instance_id, database_id, port))
capture_logs_fn = (
os.path.join(samples.SAMPLES_LOG_PATH, '{}-backend.log'.format(appname))
if capture_logs else None)
return samples.run_proc(proc_args, capture_logs_fn)
class Backend(base.Command):
"""Run the backend gRPC service for the given Cloud Spanner sample app.
This command starts the backend gRPC service for the given sample
application. Before starting the service, create the database and load any
initial data with:
$ {parent_command} init APPNAME --instance-id=INSTANCE_ID
After starting the service, generate traffic with:
$ {parent_command} workload APPNAME
To run all three steps together, use:
$ {parent_command} run APPNAME --instance-id=INSTANCE_ID
"""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To run the backend gRPC service for the 'finance' sample app using
instance 'my-instance', run:
$ {command} finance --instance-id=my-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.
"""
parser.add_argument('appname', help='The sample app name, e.g. "finance".')
parser.add_argument(
'--instance-id',
required=True,
type=str,
help='The Cloud Spanner instance ID for the sample app.')
parser.add_argument(
'--database-id',
type=str,
help='The Cloud Spanner database ID for the sample app.')
parser.add_argument(
'--duration',
default='1h',
type=arg_parsers.Duration(),
help=('Duration of time allowed to run before stopping the service.'))
parser.add_argument(
'--port', type=int, help=('Port on which to receive gRPC requests.'))
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
appname = args.appname
try:
samples.check_appname(appname)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('APPNAME', ex)
project = properties.VALUES.core.project.GetOrFail()
instance_id = args.instance_id
try:
samples_init.check_instance(instance_id)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('--instance-id', ex)
if args.database_id is not None:
database_id = args.database_id
else:
database_id = samples.get_db_id_for_app(appname)
database_ref = resources.REGISTRY.Parse(
database_id,
params={
'projectsId': project,
'instancesId': instance_id
},
collection='spanner.projects.instances.databases')
try:
databases.Get(database_ref)
except apitools_exceptions.HttpNotFoundError as ex:
if args.database_id is not None:
raise calliope_exceptions.BadArgumentException('--database-id', ex)
else:
raise samples.SpannerSamplesError(
"Database {} doesn't exist. Did you run `gcloud spanner samples "
'init` first?'.format(database_id))
proc = run_backend(project, appname, instance_id, args.database_id,
args.port)
try:
with execution_utils.RaisesKeyboardInterrupt():
proc.wait(args.duration)
except KeyboardInterrupt:
proc.terminate()
return 'Backend gRPC service killed'
except execution_utils.TIMEOUT_EXPIRED_ERR:
proc.terminate()
return 'Backend gRPC service timed out after {duration}s'.format(
duration=args.duration)
return

View File

@@ -0,0 +1,344 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner samples init."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import json
import os
import textwrap
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.spanner import database_operations
from googlecloudsdk.api_lib.spanner import database_sessions
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.api_lib.spanner import instances
from googlecloudsdk.api_lib.storage import storage_api
from googlecloudsdk.api_lib.storage import storage_util
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.spanner import ddl_parser
from googlecloudsdk.command_lib.spanner import samples
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from googlecloudsdk.core.console import progress_tracker
from googlecloudsdk.core.util import files
from googlecloudsdk.core.util import retry
def check_instance(instance_id):
"""Raise if the given instance doesn't exist."""
try:
instances.Get(instance_id)
except apitools_exceptions.HttpNotFoundError:
raise ValueError(
textwrap.dedent("""\
Instance '{instance_id}' does not exist. Create it with:
$ gcloud spanner instances create {instance_id}
""".format(instance_id=instance_id)))
def download_sample_files(appname):
"""Download schema and binaries for the given sample app.
If the schema and all binaries exist already, skip download. If any file
doesn't exist, download them all.
Args:
appname: The name of the sample app, should exist in samples.APP_NAMES
"""
storage_client = storage_api.StorageClient()
bucket_ref = storage_util.BucketReference.FromUrl(samples.GCS_BUCKET)
# Get the GCS object ref and local path for each file
gcs_to_local = [(storage_util.ObjectReference.FromBucketRef(
bucket_ref, samples.get_gcs_schema_name(appname)),
samples.get_local_schema_path(appname))]
gcs_bin_msgs = storage_client.ListBucket(
bucket_ref, prefix=samples.get_gcs_bin_prefix(appname))
bin_path = samples.get_local_bin_path(appname)
for gcs_ref in gcs_bin_msgs:
# Skip folder or dir result in ListBucket result.
if not gcs_ref.name.split('/')[-1]:
continue
gcs_ref = storage_util.ObjectReference.FromMessage(gcs_ref)
local_path = os.path.join(bin_path, gcs_ref.name.split('/')[-1])
gcs_to_local.append((gcs_ref, local_path))
if samples.has_sample_data_statements(appname):
insert_path = samples.get_gcs_data_insert_statements_prefix(appname)
gcs_insert_files = storage_client.ListBucket(bucket_ref, prefix=insert_path)
for insert_file in gcs_insert_files:
insert_file_ref = storage_util.ObjectReference.FromMessage(insert_file)
# Skip folder or dir in ListBucket result. Cannot use `os.path.isdir` to
# check due to GCS file naming convention.
if insert_file_ref.name.endswith('/'):
continue
data_local_path = samples.get_local_data_insert_statements_path(appname)
local_path = os.path.join(
data_local_path, insert_file_ref.name.split('/')[-1]
)
gcs_to_local.append((insert_file_ref, local_path))
# Download all files again if any file is missing
if any(not os.path.exists(file_path) for _, file_path in gcs_to_local):
log.status.Print('Downloading files for the {} sample app'.format(appname))
for gcs_ref, local_path in gcs_to_local:
log.status.Print('Downloading {}'.format(local_path))
local_dir = os.path.split(local_path)[0]
if not os.path.exists(local_dir):
files.MakeDir(local_dir)
storage_client.CopyFileFromGCS(gcs_ref, local_path, overwrite=True)
def _create_db_op(instance_ref, database_id, statements, database_dialect):
"""Wrapper over databases.Create with error handling."""
try:
return databases.Create(
instance_ref,
database_id,
statements,
database_dialect=database_dialect)
except apitools_exceptions.HttpConflictError:
raise ValueError(
textwrap.dedent("""\
Database '{database_id}' exists already. Delete it with:
$ gcloud spanner databases delete {database_id} --instance={instance_id}
""".format(
database_id=database_id, instance_id=instance_ref.instancesId)))
except apitools_exceptions.HttpError as ex:
raise ValueError(json.loads(ex.content)['error']['message'])
except Exception: # pylint: disable=broad-except
raise ValueError("Failed to create database '{}'.".format(database_id))
def insert_sample_data_in_one_file(appname, file_name, session_ref):
"""Read and execute all insert statements in one file."""
if not samples.has_sample_data_statements(appname):
raise ValueError('{} cannot pre-populate data.'.format(appname))
insert_statements = files.ReadFileContents(file_name)
for insert_statement in insert_statements.split('\n'):
if not insert_statement:
continue
if not insert_statement.startswith('INSERT'):
continue
# Use a retryer to handle 409 txn abort errors that tend to happen
# when db is just created and group assignment contends with insert and
# commit dual-trip txns.
retry.Retryer(max_retrials=5).RetryOnException(
database_sessions.ExecuteSql,
args=[insert_statement, 'NORMAL', session_ref],
should_retry_if=lambda exc_type, *args: True,
sleep_ms=2000,
)
def insert_sample_data(appname, database_id, session_ref):
"""Insert sample data."""
if not samples.has_sample_data_statements(appname):
raise ValueError('{} cannot pre-populate data.'.format(appname))
with progress_tracker.ProgressTracker(
'Populating data into `{}`'.format(database_id),
aborted_message='Aborting wait for data population.\n',
):
data_files = files.GetDirectoryTreeListing(
samples.get_local_data_insert_statements_path(appname)
)
for data_file in data_files:
insert_sample_data_in_one_file(
appname,
data_file,
session_ref,
)
def check_create_db(appname, instance_ref, database_id):
"""Create the DB if it doesn't exist already, raise otherwise."""
schema_file = samples.get_local_schema_path(appname)
database_dialect = samples.get_database_dialect(appname)
schema = files.ReadFileContents(schema_file)
# Special case for POSTGRESQL dialect:
# a. CreateDatabase does not support additional_statements. Instead a
# separate call to UpdateDDL is used.
# b. ddl_parser only supports GSQL; instead remove comment lines, then
# split on ';'.
if database_dialect == databases.DATABASE_DIALECT_POSTGRESQL:
create_ddl = []
# Remove comments
schema = '\n'.join(
[line for line in schema.split('\n') if not line.startswith('--')])
# TODO(b/195711543): This would be incorrect if ';' is inside strings
# and / or comments.
update_ddl = [stmt for stmt in schema.split(';') if stmt]
else:
create_ddl = ddl_parser.PreprocessDDLWithParser(schema)
update_ddl = []
create_op = _create_db_op(instance_ref, database_id, create_ddl,
database_dialect)
database_operations.Await(create_op,
"Creating database '{}'".format(database_id))
if update_ddl:
database_ref = resources.REGISTRY.Parse(
database_id,
params={
'instancesId': instance_ref.instancesId,
'projectsId': instance_ref.projectsId,
},
collection='spanner.projects.instances.databases')
update_op = databases.UpdateDdl(database_ref, update_ddl)
database_operations.Await(update_op,
"Updating database '{}'".format(database_id))
@base.DefaultUniverseOnly
class Init(base.Command):
"""Initialize a Cloud Spanner sample app.
This command creates a Cloud Spanner database in the given instance for the
sample app and loads any initial data required by the application.
"""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To initialize the 'finance' sample app using instance
'my-instance', run:
$ {command} finance --instance-id=my-instance
To initialize the 'finance-graph' sample app using instance
'my-instance', run:
$ {command} finance-graph --instance-id=my-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.
"""
parser.add_argument(
'appname', help='The sample app name, e.g. "finance", "finance-graph".'
)
parser.add_argument(
'--instance-id',
required=True,
type=str,
help='The Cloud Spanner instance ID for the sample app.')
parser.add_argument(
'--database-id',
type=str,
help='ID of the new Cloud Spanner database to create for the sample '
'app.')
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
appname = args.appname
try:
samples.check_appname(appname)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('APPNAME', ex)
instance_id = args.instance_id
instance_ref = resources.REGISTRY.Parse(
instance_id,
params={
'projectsId': properties.VALUES.core.project.GetOrFail,
},
collection='spanner.projects.instances')
if args.database_id is not None:
database_id = args.database_id
else:
database_id = samples.get_db_id_for_app(appname)
# Check that the instance exists
log.status.Print("Checking instance '{}'".format(instance_id))
try:
check_instance(instance_id)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('--instance-id', ex)
# Download any missing sample app binaries from GCS, including the schema
# file we need to create the DB
download_sample_files(appname)
# Create the sample app DB
log.status.Print(
"Initializing database '{database_id}' for sample app '{appname}'"
.format(database_id=database_id, appname=appname))
try:
check_create_db(appname, instance_ref, database_id)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('--database-id', ex)
if samples.has_sample_data_statements(appname):
database_ref = resources.REGISTRY.Parse(
database_id,
params={
'instancesId': instance_ref.instancesId,
'projectsId': instance_ref.projectsId,
},
collection='spanner.projects.instances.databases',
)
session = database_sessions.Create(database_ref)
session_ref = resources.REGISTRY.ParseRelativeName(
relative_name=session.name,
collection='spanner.projects.instances.databases.sessions',
)
try:
insert_sample_data(appname, database_id, session_ref)
except Exception:
raise SystemError(
'Failed to insert data for the database. Please fallback to '
'manually insert.'
)
else:
return textwrap.dedent("""\
Initialization done for your Spanner database.
""")
finally:
database_sessions.Delete(session_ref)
else:
backend_args = '{appname} --instance-id={instance_id}'.format(
appname=appname, instance_id=instance_id
)
if args.database_id is not None:
backend_args += ' --database-id {}'.format(database_id)
return textwrap.dedent("""\
Initialization done. Next, start the backend gRPC service with:
$ gcloud spanner samples backend {}
""".format(backend_args))

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner samples list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import samples
class List(base.ListCommand):
"""List available sample applications."""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To list available sample applications, run:
$ {command}
"""),
}
@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.
"""
pass
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return list(sorted(samples.APPS))

View File

@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner samples run."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
import time
from apitools.base.py import exceptions as apitools_exceptions
from googlecloudsdk.api_lib.spanner import databases
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.calliope import exceptions as calliope_exceptions
from googlecloudsdk.command_lib.spanner import samples
from googlecloudsdk.core import execution_utils
from googlecloudsdk.core import log
from googlecloudsdk.core import properties
from googlecloudsdk.core import resources
from surface.spanner.samples import backend as samples_backend
from surface.spanner.samples import init as samples_init
from surface.spanner.samples import workload as samples_workload
class Run(base.Command):
"""Run the given Cloud Spanner sample app.
Each Cloud Spanner sample application includes a backend gRPC service
backed by a Cloud Spanner database and a workload script that generates
service traffic. This command creates and initializes the Cloud Spanner
database and runs both the backend service and workload script.
These sample apps are open source and available at
https://github.com/GoogleCloudPlatform/cloud-spanner-samples.
To see a list of available sample apps, run:
$ {parent_command} list
"""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To run the 'finance' sample app using instance 'my-instance', run:
$ {command} finance --instance-id=my-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.
"""
parser.add_argument('appname', help='The sample app name, e.g. "finance".')
parser.add_argument(
'--instance-id',
required=True,
type=str,
help='The Cloud Spanner instance ID for the sample app.')
parser.add_argument(
'--database-id',
type=str,
help='ID of the new Cloud Spanner database to create for the sample '
'app.')
parser.add_argument(
'--duration',
default='1h',
type=arg_parsers.Duration(),
help=('Duration of time allowed to run the sample app before stopping '
'the service.'))
parser.add_argument(
'--cleanup',
action='store_true',
default=True,
help=('Delete the instance after running the sample app.'))
parser.add_argument(
'--skip-init',
action='store_true',
default=False,
help=('Use an existing database instead of creating a new one.'))
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
appname = args.appname
try:
samples.check_appname(appname)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('APPNAME', ex)
instance_id = args.instance_id
project = properties.VALUES.core.project.GetOrFail()
instance_ref = resources.REGISTRY.Parse(
instance_id,
params={
'projectsId': project,
},
collection='spanner.projects.instances')
if args.database_id is not None:
database_id = args.database_id
else:
database_id = samples.get_db_id_for_app(appname)
duration = args.duration
skip_init = args.skip_init
try:
samples_init.check_instance(instance_id)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('--instance-id', ex)
log.status.Print(
"Initializing database '{database_id}' for sample app '{appname}'"
.format(database_id=database_id, appname=appname))
if skip_init:
database_ref = resources.REGISTRY.Parse(
database_id,
params={
'instancesId': instance_id,
'projectsId': project
},
collection='spanner.projects.instances.databases')
try:
databases.Get(database_ref)
# --skip-init assumes the database exists already, raise if it doesn't.
except apitools_exceptions.HttpNotFoundError:
bad_flag = ('--instance-id'
if args.database_id is None else '--database-id')
raise calliope_exceptions.BadArgumentException(
bad_flag, "Database '{database_id}' does not exist in instance "
"'{instance_id}'. Re-run this command without `--skip-init` to "
'create it.'.format(
database_id=database_id, instance_id=instance_id))
else:
try:
# Download any missing sample files and create the DB.
if self.ReleaseTrack() == base.ReleaseTrack.ALPHA:
samples_init.download_sample_files(args.appname)
samples_init.check_create_db(args.appname, instance_ref, database_id)
except ValueError as ex:
raise calliope_exceptions.BadArgumentException('--database-id', ex)
be_proc = samples_backend.run_backend(project, appname, instance_id,
database_id)
try:
be_proc.wait(2)
return (
'The {} sample app backend gRPC service failed to start, is another '
'instance already running?'.format(appname))
except execution_utils.TIMEOUT_EXPIRED_ERR:
pass
now = int(time.time())
later = now + duration
wl_proc = samples_workload.run_workload(appname, capture_logs=True)
# Wait a second to let the workload print startup logs
time.sleep(1)
log.status.Print(
'\nGenerating workload for database, start timestamp: {now}, end '
'timestamp: {later}. Press ^C to stop.'.format(now=now, later=later))
try:
with execution_utils.RaisesKeyboardInterrupt():
wl_proc.wait(duration)
except KeyboardInterrupt:
wl_proc.terminate()
be_proc.terminate()
log.status.Print('Backend gRPC service and workload generator killed')
except execution_utils.TIMEOUT_EXPIRED_ERR:
wl_proc.terminate()
be_proc.terminate()
log.status.Print(
'Backend gRPC service and workload generator killed after {duration}s'
.format(duration=duration))
if args.cleanup:
log.status.Print("Deleting database '{}'".format(database_id))
database_ref = resources.REGISTRY.Parse(
database_id,
params={
'projectsId': properties.VALUES.core.project.GetOrFail,
'instancesId': instance_ref.instancesId
},
collection='spanner.projects.instances.databases')
databases.Delete(database_ref)
log.status.Print('Done')
return

View File

@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google LLC. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Command for spanner samples workload."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
import textwrap
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import samples
from googlecloudsdk.core import execution_utils
def _get_popen_jar(appname):
if appname not in samples.APPS:
raise ValueError("Unknown sample app '{}'".format(appname))
return os.path.join(
samples.get_local_bin_path(appname), samples.APPS[appname].workload_bin)
def run_workload(appname, port=None, capture_logs=False):
"""Run the workload generator executable for the given sample app.
Args:
appname: str, Name of the sample app.
port: int, Port to run the service on.
capture_logs: bool, Whether to save logs to disk or print to stdout.
Returns:
subprocess.Popen or execution_utils.SubprocessTimeoutWrapper, The running
subprocess.
"""
proc_args = ['java', '-jar', _get_popen_jar(appname)]
if port is not None:
proc_args.append('--port={}'.format(port))
capture_logs_fn = (
os.path.join(samples.SAMPLES_LOG_PATH, '{}-workload.log'.format(appname))
if capture_logs else None)
return samples.run_proc(proc_args, capture_logs_fn)
class Workload(base.Command):
"""Generate gRPC traffic for a given sample app's backend service.
Before sending traffic to the backend service, create the database and
start the service with:
$ {parent_command} init APPNAME --instance-id=INSTANCE_ID
$ {parent_command} backend APPNAME --instance-id=INSTANCE_ID
To run all three steps together, use:
$ {parent_command} run APPNAME --instance-id=INSTANCE_ID
"""
detailed_help = {
'EXAMPLES':
textwrap.dedent("""\
To generate traffic for the 'finance' sample app, run:
$ {command} finance
"""),
}
@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('appname', help='The sample app name, e.g. "finance".')
parser.add_argument(
'--duration',
default='1h',
type=arg_parsers.Duration(),
help=('Duration of time allowed to run before stopping the workload.'))
parser.add_argument(
'--port', type=int, help=('Port of the running backend service.'))
parser.add_argument(
'--target-qps', type=int, help=('Target requests per second.'))
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
proc = run_workload(args.appname, args.port)
try:
with execution_utils.RaisesKeyboardInterrupt():
return proc.wait(args.duration)
except KeyboardInterrupt:
proc.terminate()
return 'Workload generator killed'
except execution_utils.TIMEOUT_EXPIRED_ERR:
proc.terminate()
return 'Workload generator killed after {duration}s'.format(
duration=args.duration)
return

View File

@@ -0,0 +1,29 @@
# -*- 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.
"""Command group for spanner SSD caches."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.calliope import base
@base.ReleaseTracks(base.ReleaseTrack.ALPHA)
@base.Hidden
class SsdCaches(base.Group):
"""Manage Cloud Spanner SSD caches."""
pass

View File

@@ -0,0 +1,64 @@
# -*- 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.
"""Command for spanner SSD caches describe."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import ssd_caches
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
class Describe(base.DescribeCommand):
"""Describe a Cloud Spanner SSD cache."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To describe a Cloud Spanner SSD cache, run:
$ {command} my-cache-id --config=my-config-id
"""),
}
@staticmethod
def Args(parser):
"""Args is called by calliope to gather arguments for this command.
Please add arguments in alphabetical order except for no- or a clear-
pair for that argument which can follow the argument itself.
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.
"""
flags.SsdCache(positional=True, required=True, hidden=False).AddToParser(
parser
)
flags.Config().AddToParser(parser)
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return ssd_caches.Get(args.cache_id, args.config)

View File

@@ -0,0 +1,62 @@
# -*- 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.
"""Command for spanner SSD caches list."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import textwrap
from googlecloudsdk.api_lib.spanner import ssd_caches
from googlecloudsdk.calliope import base
from googlecloudsdk.command_lib.spanner import flags
class List(base.ListCommand):
"""List available Cloud Spanner SSD caches."""
detailed_help = {
'EXAMPLES': textwrap.dedent("""\
To list the Cloud Spanner SSD caches in an instance config, run:
$ {command} --config=my-config-id
"""),
}
@staticmethod
def Args(parser):
flags.Config().AddToParser(parser)
base.FILTER_FLAG.RemoveFromParser(parser) # we don't support filter
parser.display_info.AddFormat("""
table(
name.basename(),
displayName,
size_gib,
labels
)
""")
def Run(self, args):
"""This is what gets called when the user runs this command.
Args:
args: an argparse namespace. All the arguments that were provided to this
command invocation.
Returns:
Some value that we want to have printed later.
"""
return ssd_caches.List(args.config)

Some files were not shown because too many files have changed in this diff Show More