File: //snap/google-cloud-cli/396/lib/surface/app/services/set_traffic.py
# -*- coding: utf-8 -*- #
# Copyright 2015 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.
"""`gcloud app services set-traffic` command."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.app import appengine_api_client
from googlecloudsdk.api_lib.app import operations_util
from googlecloudsdk.api_lib.app import service_util
from googlecloudsdk.calliope import arg_parsers
from googlecloudsdk.calliope import base
from googlecloudsdk.core import exceptions
from googlecloudsdk.core import log
from googlecloudsdk.core.console import console_io
from googlecloudsdk.core.resource import resource_printer
import six
class TrafficSplitError(exceptions.Error):
"""Errors occurring when setting traffic splits."""
pass
class SetTraffic(base.Command):
"""Set traffic splitting settings.
This command sets the traffic split of versions across a service or a project.
"""
detailed_help = {
'EXAMPLES': """\
To send all traffic to 'v2' of service 's1', run:
$ {command} s1 --splits=v2=1
To split traffic evenly between 'v1' and 'v2' of service 's1', run:
$ {command} s1 --splits=v2=.5,v1=.5
To split traffic across all services:
$ {command} --splits=v2=.5,v1=.5
""",
}
@staticmethod
def Args(parser):
parser.add_argument('services', nargs='*', help=(
'The services to modify.'))
parser.add_argument(
'--splits',
required=True,
type=arg_parsers.ArgDict(min_length=1),
help="""\
Key-value pairs describing what proportion of traffic should go to
each version. The split values are added together and used as
weights. The exact values do not matter, only their relation to each
other. For example, v1=2,v2=2 is equivalent to v1=.5,v2=.5""")
parser.add_argument(
'--split-by',
choices=['cookie', 'ip', 'random'],
default='ip',
help='Whether to split traffic based on cookie, IP address or random.')
parser.add_argument(
'--migrate',
action='store_true',
default=False,
help="""\
The migrate flag determines whether or not to use traffic migration
during the operation. Traffic migration will attempt to automatically
migrate traffic from the previous version to the new version, giving
the autoscaler time to respond. See the documentation here:
[](https://cloud.google.com/appengine/docs/python/console/trafficmigration)
for more information.""")
def Run(self, args):
if args.migrate and len(args.splits) > 1:
raise TrafficSplitError('The migrate flag can only be used with splits '
'to a single version.')
api_client = appengine_api_client.GetApiClientForTrack(self.ReleaseTrack())
all_services = api_client.ListServices()
services = service_util.GetMatchingServices(all_services, args.services)
allocations = service_util.ParseTrafficAllocations(
args.splits, args.split_by)
display_allocations = []
for service in services:
for version, split in six.iteritems(allocations):
display_allocations.append('{0}/{1}/{2}: {3}'.format(
api_client.project,
service.id,
version,
split))
fmt = 'list[title="Setting the following traffic allocation:"]'
resource_printer.Print(display_allocations, fmt, out=log.status)
log.status.Print(
'NOTE: Splitting traffic by {0}.'.format(args.split_by))
log.status.Print('Any other versions of the specified service will '
'receive zero traffic.')
console_io.PromptContinue(cancel_on_no=True)
errors = {}
for service in services:
try:
operations_util.CallAndCollectOpErrors(
api_client.SetTrafficSplit, service.id, allocations,
args.split_by.upper(), args.migrate)
except operations_util.MiscOperationError as err:
errors[service.id] = six.text_type(err)
if errors:
printable_errors = {}
for service, error_msg in errors.items():
printable_errors[service] = error_msg
raise TrafficSplitError(
'Issue setting traffic on service(s): {0}\n\n'.format(
', '.join(list(printable_errors.keys()))) +
'\n\n'.join(list(printable_errors.values())))