def testZeroSum(self): """Tests that we fail if all allocations are zero.""" split = {"v1": 0.0, "v2": 0.0} with self.assertRaises(service_util.ServicesSplitTrafficError): service_util.ParseTrafficAllocations(split, DECIMAL_PRECISION)
def testSingleRoundedDownToZero(self): """Tests that we fail if one allocation is rounded down to zero.""" split = {"v1": 1.0, "v2": 0.0001} with self.assertRaises(service_util.ServicesSplitTrafficError): service_util.ParseTrafficAllocations(split, DECIMAL_PRECISION)
def testSmallNumbersMatter(self): """Tests that if we have two tiny numbers with a good split, it's ok.""" split = {"v1": 0.00008, "v2": 0.00008} result = service_util.ParseTrafficAllocations(split, DECIMAL_PRECISION) expected_result = {"v1": 0.500, "v2": 0.500} self.assertEqual(result, expected_result)
def testLessThanOneHundredIsRoundedUp(self): """Tests that allocations which sum less than 100 are rounded up to 100.""" split = {"v1": 33, "v2": 33, "v3": 33} result = service_util.ParseTrafficAllocations(split, DECIMAL_PRECISION) # v1 is expected to round up, since it is the first version in a sort and # all allocations are equally max. expected_result = {"v1": 0.334, "v2": 0.333, "v3": 0.333} self.assertEqual(result, expected_result)
def testSplitRespectsDecimalPrecision(self): """Tests that unparsed traffic splits properly respect precision.""" # These allocations sum to 0.9999999999999999 unparsed_allocations = {"v1": 50, "v2": 41, "v3": 9} result = service_util.ParseTrafficAllocations(unparsed_allocations, DECIMAL_PRECISION) expected_result = {"v1": 0.5, "v2": 0.41, "v3": 0.09} self.assertEqual(result, expected_result)
def testMaximumElementIsRounded(self): """Tests that the maximum value is picked as the element that is modified. This ensures that we don't mistakenly round the first value down to zero. """ split = {"v1": 0.1, "v2": 49, "v3": 51} result = service_util.ParseTrafficAllocations(split, DECIMAL_PRECISION) expected_result = {"v1": 0.001, "v2": 0.49, "v3": 0.509} self.assertEqual(result, expected_result)
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] = str(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())))
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.GetApiClient(self.Http(timeout=None)) all_services = api_client.ListServices() services = service_util.GetMatchingServices(all_services, args.services, api_client.project) allocations = service_util.ParseTrafficAllocations( args.splits, args.split_by) display_allocations = [] for service in services: for version, split in allocations.iteritems(): display_allocations.append('{0}/{1}/{2}: {3}'.format( api_client.project, service.id, version, split)) printer = console_io.ListPrinter( 'Setting the following traffic allocations:') printer.Print(display_allocations, output_stream=log.status) log.status.Print('Any other versions on the specified services will ' 'receive zero traffic.') console_io.PromptContinue(cancel_on_no=True) errors = {} for service in services: try: api_client.SetTrafficSplit(service.id, allocations, args.split_by.upper(), args.migrate) except (calliope_exceptions.HttpException, operations.OperationError, operations.OperationTimeoutError) as err: errors[service.id] = str(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(printable_errors.keys())) + '\n\n'.join(printable_errors.values()))