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() 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 allocations.iteritems(): display_allocations.append('{0}/{1}/{2}: {3}'.format( api_client.project, service.id, version, split)) fmt = 'list[title="Setting the following traffic allocations:"]' resource_printer.Print(display_allocations, fmt, out=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_util.OperationError, operations_util.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()))
def testResultsWithMultipleTestSuites(self): summary_fetcher = self._createResultsSummaryFetcher() environment1 = self._createEnvironment( SUCCESS_OUTCOME, [PASS_OVERVIEW1, PASS_OVERVIEW2]) environment2 = self._createEnvironment( FAILURE_OUTCOME, [FAIL_OVERVIEW1, FAIL_OVERVIEW2]) self._expectHistoriesExecutionsEnvironmentsList( [environment1, environment2]) outcomes = summary_fetcher.CreateMatrixOutcomeSummaryUsingEnvironments( ) resource_printer.Print(outcomes, TEST_OUTCOME_FORMAT) self.AssertOutputContains( """Failed | Nexus-v1-en-up | 5 test cases failed, 5 passed, 2 errors, \ 1 skipped | | Passed | Nexus-v1-en-up | 21 test cases passed, 1 skipped""", normalize_space=True)
def ListCompletionTree(cli, branch=None, out=None): """Lists the static completion CLI tree as a Python module file. Args: cli: The CLI. branch: The path of the CLI subtree to generate. out: The output stream to write to, sys.stdout by default. Returns: Returns the serialized static completion CLI tree. """ tree = GenerateCompletionTree(cli=cli, branch=branch) (out or sys.stdout).write('''\ # -*- coding: utf-8 -*- # """Cloud SDK static completion CLI tree.""" # pylint: disable=line-too-long,bad-continuation STATIC_COMPLETION_CLI_TREE = ''') resource_printer.Print(tree, print_format='json', out=out) return tree
def DisplayQueryResults(result, out): """Prints the result rows for a query. Args: result (spanner_v1_messages.ResultSet): The server response to a query. out: Output stream to which we print. """ # Print "(Unspecified)" for computed columns. fields = [ field.name or '(Unspecified)' for field in result.metadata.rowType.fields ] # Create the format string we pass to the table layout. table_format = ','.join('row.slice({0}).join():label="{1}"'.format(i, f) for i, f in enumerate(fields)) rows = [{'row': encoding.MessageToPyValue(row.entry)} for row in result.rows] # Can't use the PrintText method because we want special formatting. resource_printer.Print(rows, 'table({0})'.format(table_format), out=out)
def testDescribe(self): test_zone = util.GetManagedZones()[0] test_change = util.GetChanges()[1] self.mocked_dns_v1.changes.Get.Expect( self.messages.DnsChangesGetRequest( changeId=test_change.id, managedZone=test_zone.name, project=self.Project()), test_change) result = self.Run('dns record-sets changes describe -z {0} {1}'.format( test_zone.name, test_change.id)) self.assertEqual(test_change, result) expected_output = io.StringIO() resource_printer.Print( test_change, 'yaml', out=expected_output, single=True) self.AssertOutputContains(expected_output.getvalue()) expected_output.close()
def testCommandTreeGeneratorRestrict(self): """Test the list of the sdk groups and commands.""" result = walker_util.CommandTreeGenerator(self.cli).Walk( restrict=['gcloud.sdk']) resource_printer.Print(result, print_format='json') self.AssertOutputContains("""\ { "_name_": "gcloud", "groups": [ { "_name_": "sdk", "commands": [ { "_name_": "ordered-choices" }, { "_name_": "second-level-command-1" }, { "_name_": "second-level-command-b" }, { "_name_": "xyzzy" } ], "groups": [ { "_name_": "subgroup", "commands": [ { "_name_": "subgroup-command-2" }, { "_name_": "subgroup-command-a" } ] } ] } ] } """)
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. Raises: HttpException: An http error response was received while executing api request. Returns: None """ op = None apitools_client = genomics_util.GetGenomicsClient('v2alpha1') genomics_messages = genomics_util.GetGenomicsMessages('v2alpha1') name, v2 = genomics_util.CanonicalizeOperationName(args.name) if v2: op = apitools_client.projects_operations.Get( genomics_messages.GenomicsProjectsOperationsGetRequest(name=name)) else: apitools_client = genomics_util.GetGenomicsClient() genomics_messages = genomics_util.GetGenomicsMessages() op = apitools_client.operations.Get( genomics_messages.GenomicsOperationsGetRequest(name=name)) operation_string = io.StringIO() print_format = display.Displayer(self, args).GetFormat() resource_printer.Print(op, print_format, out=operation_string) if not console_io.PromptContinue(message='%s\n%s' % ( operation_string.getvalue(), 'This operation will be canceled')): raise GenomicsError('Cancel aborted by user.') if v2: apitools_client.projects_operations.Cancel( genomics_messages.GenomicsProjectsOperationsCancelRequest(name=name)) else: apitools_client.operations.Cancel( genomics_messages.GenomicsOperationsCancelRequest(name=name)) log.status.write('Canceled [{0}].\n'.format(name))
def get_field(self, field_name, unused_args, unused_kwargs): r"""Returns the value of field_name for string.Formatter.format(). Args: field_name: The format string field name to get in the form name - the value of name in the payload, '' if undefined name?FORMAT - if name is non-empty then re-formats with FORMAT, where {?} is the value of name. For example, if name=NAME then {name?\nname is "{?}".} expands to '\nname is "NAME".'. .a.b.c - the value of a.b.c in the JSON decoded payload contents. For example, '{.errors.reason?[{?}]}' expands to [REASON] if .errors.reason is defined. unused_args: Ignored. unused_kwargs: Ignored. Returns: The value of field_name for string.Formatter.format(). """ field_name = _Expand(field_name) if field_name == '?': return self._value, field_name parts = field_name.split('?', 1) subparts = parts.pop(0).split(':', 1) name = subparts.pop(0) printer_format = subparts.pop(0) if subparts else None recursive_format = parts.pop(0) if parts else None name, value = self._GetField(name) if not value and not isinstance(value, (int, float)): return '', name if printer_format or not isinstance( value, (six.text_type, six.binary_type, float) + six.integer_types): buf = io.StringIO() resource_printer.Print(value, printer_format or 'default', out=buf, single=True) value = buf.getvalue().strip() if recursive_format: self._value = value value = self.format(_Expand(recursive_format)) return value, name
def testResultsForFlakyEnvironment(self): summary_fetcher = self._createResultsSummaryFetcher() environment = self._createEnvironment(FLAKY_OUTCOME, [FLAKY_OVERVIEW]) self.tr_client.projects_histories_executions_environments.List.Expect( request=self._createEnvironmentsListRequest(None), response=self.toolresults_msgs.ListEnvironmentsResponse( environments=[environment])) outcomes = summary_fetcher.CreateMatrixOutcomeSummaryUsingEnvironments( ) resource_printer.Print(outcomes, TEST_OUTCOME_FORMAT) self.AssertOutputContains( '| OUTCOME | TEST_AXIS_VALUE | TEST_DETAILS |', normalize_space=True) self.AssertOutputContains( '| Flaky | Nexus-v1-en-up | 3 test cases flaky, 1 passed |', normalize_space=True) self.AssertErrEquals('')
def _Generate(): """Helper that generates a CLI tree and writes it to a JSON file.""" tree = self.Generate() if tree: try: f = files.FileWriter(path) except files.Error as e: # CLI data config dir may not be initialized yet. directory, _ = os.path.split(path) try: files.MakeDir(directory) f = files.FileWriter(path) except files.Error: if not warn_on_exceptions: raise log.warning(six.text_type(e)) return None with f: resource_printer.Print(tree, print_format='json', out=f) return tree
def _Generate(): """Helper that generates a CLI tree and writes it to a JSON file.""" tree = self.Generate() if tree: try: f = open(path, 'w') except EnvironmentError as e: # CLI data config dir may not be initialized yet. directory, _ = os.path.split(path) try: files.MakeDir(directory) f = open(path, 'w') except (EnvironmentError, files.Error): if not warn_on_exceptions: raise log.warn(str(e)) return None with f: resource_printer.Print(tree, print_format='json', out=f) return tree
def Table(self, table, rows): """Renders a table. Nested tables are not supported. Args: table: A TableAttributes object. rows: A list of rows where each row is a list of column strings. """ self.Line() indent = self._indent[self._level].indent + 2 margin = indent if any([True for r in rows if ' ' in r[-1]]) else 0 buf = io.StringIO() resource_printer.Print(rows, table.GetPrintFormat(margin=margin), out=buf) for line in buf.getvalue().split('\n')[:-1]: self.TableLine(line, indent=indent) self.Content() self.Line()
def testUseStepResultsIfNoEnvironmentResultsAreAvailable(self): summary_fetcher = self._createResultsSummaryFetcher() self._expectHistoriesExecutionsEnvironmentsList([]) step = self._createStep(FAILURE_TIMEOUT_OUTCOME, [FAIL_OVERVIEW1]) self._expectHistoriesExecutionsStepsList([step]) outcomes = summary_fetcher.CreateMatrixOutcomeSummaryUsingEnvironments( ) resource_printer.Print(outcomes, TEST_OUTCOME_FORMAT) self.AssertErrContains( """WARNING: Environment has no results, something went wrong. Displaying step outcomes instead.""" ) self.AssertOutputContains( '| OUTCOME | TEST_AXIS_VALUE | TEST_DETAILS |', normalize_space=True) self.AssertOutputContains( '| Failed | Nexus-v1-en-up | Test timed out |', normalize_space=True)
def Run(self, args): project = properties.VALUES.core.project.Get(required=True) app_config = yaml_parsing.AppConfigSet([args.index_file]) if yaml_parsing.ConfigYamlInfo.INDEX not in app_config.Configs(): raise exceptions.InvalidArgumentException( 'index_file', 'You must provide the path to a valid index.yaml file.') info = app_config.Configs()[yaml_parsing.ConfigYamlInfo.INDEX] fmt = 'list[title="You are about to update the following configurations:"]' resource_printer.Print( ['{0}/{1} From: [{2}]'.format(project, info.config, info.file)], fmt, out=log.status) console_io.PromptContinue(default=True, throw_if_unattended=False, cancel_on_no=True) client = appengine_client.AppengineClient() client.UpdateIndexes(info.parsed)
def _PrintPendingAction(self, components, action): """Prints info about components we are going to install or remove. Args: components: list(schemas.Component), The components that are going to be acted on. action: str, The verb to print for this set of components. """ attributes = [ 'box', 'no-empty-legend', 'title="These components will be {action}."'.format(action=action), ] columns = [ 'details.display_name:label=Name:align=left', 'version.version_string:label=Version:align=right', 'data.size.size(zero="",min=1048576):label=Size:align=right', ] fmt = 'table[{attributes}]({columns})'.format( attributes=','.join(attributes), columns=','.join(columns)) resource_printer.Print(components, fmt)
def Run(self, args): # resource represents the Managed Microsoft AD operation. resource = args.CONCEPTS.name.Parse() client = util.GetClientForResource(resource) messages = util.GetMessagesForResource(resource) get_req = \ messages.ManagedidentitiesProjectsLocationsGlobalOperationsGetRequest( name=resource.RelativeName()) op = client.projects_locations_global_operations.Get(get_req) operation_string = io.StringIO() print_format = display.Displayer(self, args).GetFormat() resource_printer.Print(op, print_format, out=operation_string) if not console_io.PromptContinue( message='{}\nThis operation will be canceled'.format( operation_string.getvalue())): raise exceptions.ActiveDirectoryError('Cancel aborted by user.') cancel_req = \ messages.ManagedidentitiesProjectsLocationsGlobalOperationsCancelRequest( name=resource.RelativeName()) client.projects_locations_global_operations.Cancel(cancel_req) log.status.write('Canceled [{0}].\n'.format(resource.RelativeName()))
def PromptForPublicContactsAck(domain, contacts, print_format='default'): """Asks a user for Public Contacts Ack. Args: domain: Domain name. contacts: Current Contacts. All 3 contacts should be present. print_format: Print format, e.g. 'default' or 'yaml'. Returns: Boolean: whether the user accepted the notice or not. """ log.status.Print( 'You choose to make contact data of domain {} public.\n' 'Anyone who looks it up in the WHOIS directory will be able to see info\n' 'for the domain owner and administrative and technical contacts.\n' 'Make sure it\'s ok with them that their contact data is public.\n' 'This info will be publicly available:'.format(domain)) contacts = _SimplifyContacts(contacts) resource_printer.Print(contacts, print_format, out=sys.stderr) return console_io.PromptContinue( message=None, default=False, throw_if_unattended=True, cancel_on_no=True)
def Display(self, args, resources): """API response display logic.""" if args.expand_table: # Removes unwanted "transferJobs/" and "transferOperations/" prefixes. # Extract start date from start time string. # Remove "s" from repeatInterval seconds and make ISO duration string. format_string = """table( name.slice(19:).join(sep=''), metadata.startTime.date('%Y-%m-%d'):label='START DATE', metadata.counters.bytesCopiedToSink.size():label='DATA COPIED', metadata.status, metadata.errorBreakdowns.yesno(yes='Yes'):label='HAS ERRORS', metadata.transferJobName.slice(13:).join( sep=''):label='TRANSFER JOB NAME') """ else: format_string = """table( name.slice(19:).join(sep=''), metadata.startTime.date('%Y-%m-%d'):label='START DATE', metadata.status) """ resource_printer.Print(resources, args.format or format_string)
def Run(self, args): # TODO(b/36052475): This fails with "module/version does not exist" even # when it exists if the scaling mode is set to auto. It would be good # to improve that error message. api_client = appengine_api_client.GetApiClientForTrack( self.ReleaseTrack()) services = api_client.ListServices() versions = version_util.GetMatchingVersions( api_client.ListVersions(services), args.versions, args.service) if not versions: log.warn('No matching versions found.') return fmt = 'list[title="Starting the following versions:"]' resource_printer.Print(versions, fmt, out=log.status) console_io.PromptContinue(cancel_on_no=True) errors = {} # Sort versions to make behavior deterministic enough for unit testing. for version in sorted(versions): try: with progress_tracker.ProgressTracker( 'Starting [{0}]'.format(version)): api_client.StartVersion(version.service, version.id) except (calliope_exceptions.HttpException, operations_util.OperationError, operations_util.OperationTimeoutError) as err: errors[version] = str(err) if errors: printable_errors = {} for version, error_msg in errors.items(): short_name = '[{0}/{1}]'.format(version.service, version.id) printable_errors[short_name] = '{0}: {1}'.format( short_name, error_msg) raise VersionsStartError( 'Issues starting version(s): {0}\n\n'.format(', '.join( printable_errors.keys())) + '\n\n'.join(printable_errors.values()))
def DisplayQueryAggregateStats(query_stats, out): """Displays the aggregate stats for a Spanner SQL query. Looks at the queryStats portion of the query response and prints some of the aggregate statistics. Args: query_stats (spanner_v1_messages.ResultSetStats.QueryStatsValue): The query stats taken from the server response to a query. out: Output stream to which we print. """ get_prop = partial(_GetAdditionalProperty, query_stats.additionalProperties) stats = { 'total_elapsed_time': _ConvertToStringValue(get_prop('elapsed_time')), 'cpu_time': _ConvertToStringValue(get_prop('cpu_time')), 'rows_returned': _ConvertToStringValue(get_prop('rows_returned')), 'rows_scanned': _ConvertToStringValue(get_prop('rows_scanned')) } resource_printer.Print( stats, 'table[box](total_elapsed_time, cpu_time, rows_returned, rows_scanned)', out=out)
def Display(self): """The default display method.""" if not log.IsUserOutputEnabled(): log.debug('Display disabled.') # NOTICE: Do not consume resources here. Some commands use this case to # access the results of Run() via the return value of Execute(). return # Determine the format. fmt = self._GetFormat() if fmt: # Most command output will end up here. log.debug('Display format "%s".', fmt) # TODO(gsfowler): b/24267426 if self._resources is not None: resource_printer.Print(self._resources, fmt, out=log.out) else: # This will eventually be rare. log.debug('Explict Display.') self._command.Display(self._args, self._resources)
def LoadOrGenerate(self, directories, verbose=False, warn_on_exceptions=False): """Loads the CLI tree or generates it if it's out of date.""" if not self.CliCommandExists(): if verbose: log.warn(u'Command [{}] not found.'.format(self.cli_name)) return None up_to_date = False path, f = self.FindTreeFile(directories) if f: with f: try: tree = json.load(f) except ValueError: # Corrupt JSON -- could have been interrupted. tree = None if tree: version = self.GetVersion() up_to_date = tree.get(cli_tree.LOOKUP_CLI_VERSION) == version if up_to_date: if verbose: log.status.Print(u'[{}] CLI tree version [{}] is up to date.'.format( self.cli_name, version)) return tree with progress_tracker.ProgressTracker( u'{} the [{}] CLI tree'.format( 'Updating' if f else 'Generating', self.cli_name)): tree = self.GenerateTree() try: f = open(path, 'w') except IOError as e: if not warn_on_exceptions: raise log.warn(str(e)) else: with f: resource_printer.Print(tree, print_format='json', out=f)
def testPrintDateTime(self): resource = [{ 'start': datetime.datetime(2015, 10, 21, 10, 11, 12, 0), }] # blech: Python 3.6 adds a fold "bool" that takes value [0, 1]. has_fold = hasattr(resource[0]['start'], 'fold') resource_printer.Print(resource, 'json') self.AssertOutputEquals( textwrap.dedent("""\ [ {{ "start": {{ "datetime": "2015-10-21 10:11:12", "day": 21,{fold} "hour": 10, "microsecond": 0, "minute": 11, "month": 10, "second": 12, "year": 2015 }} }} ] """.format(fold='\n "fold": 0,' if has_fold else '')))
def testFloat(self): resource_printer.Print(self.float_resource, 'yaml', single=True) self.AssertOutputEquals(textwrap.dedent("""\ a: 1.0 b: -1.0 c: 1.00001 d: -1.00009 e: 1.0009 f: -1.009 g: 1.009 h: -1.09 i: 1.9 j: -1.33333 k: 1.66667 l: -12.3457 m: 123.457 n: -1234.57 o: 12345.7 p: -123456.8 q: 1234567.9 r: -12345678.9 s: 123456789.0 t: -1.23457e+09 """))
def PrettyPrint(resource, print_format='json'): """Prints the given resource.""" resource_printer.Print( resources=[resource], print_format=print_format, out=log.out)
def ConstructList(title, items): buf = io.StringIO() resource_printer.Print(items, 'list[title="{0}"]'.format(title), out=buf) return buf.getvalue()
def Display(self, args, resources): resource_printer.Print(resources, 'json')
def ConstructList(title, items): """Returns a string displaying the items and a title.""" buf = io.StringIO() fmt = 'list[title="{title}",always-display-title]'.format(title=title) resource_printer.Print(sorted(set(items)), fmt, out=buf) return buf.getvalue()
def Display(self, args, resources): del args # Unsued. resource_printer.Print(resources, 'json')
def PromptForNameServers(domain, enable_dnssec=None, dns_settings=None, print_format='default'): """Asks the user to provide DNS settings interactively. Args: domain: Domain name corresponding to the DNS settings. enable_dnssec: Should the DNSSEC be enabled. dns_settings: Current DNS configuration (or None if resource is not yet created). print_format: Print format to use when showing current dns_settings. Returns: A pair: (messages.DnsSettings, DnsUpdateMask) to be updated, or (None, None) if the user cancelled. """ options = [ 'Provide name servers list', 'Provide Cloud DNS Managed Zone name', 'Use free name servers provided by Google Domains' ] if dns_settings is not None: log.status.Print('Your current DNS settings are:') resource_printer.Print(dns_settings, print_format, out=sys.stderr) cancel_option = True default = len(options) # Additional 'cancel' option. else: cancel_option = False default = 1 # Cloud DNS Zone. message = ('You can provide your DNS settings by specifying name servers, ' 'Cloud DNS Managed Zone name or by choosing ' 'free name servers provided by Google Domains') index = console_io.PromptChoice(message=message, options=options, cancel_option=cancel_option, default=default) name_servers = [] if index == 0: # name servers. while len(name_servers) < 2: while True: ns = console_io.PromptResponse( 'Name server (empty line to finish): ') if not ns: break if not util.ValidateDomainName(ns): log.status.Print('Invalid name server: \'{}\'.'.format(ns)) else: name_servers += [ns] if len(name_servers) < 2: log.status.Print( 'You have to provide at least 2 name servers.') return _CustomNameServers(name_servers) elif index == 1: # Cloud DNS. while True: zone = util.PromptWithValidator( validator=util.ValidateNonEmpty, error_message=' Cloud DNS Managed Zone name must not be empty.', prompt_string='Cloud DNS Managed Zone name: ') try: name_servers, ds_records = _GetCloudDnsDetails( zone, domain, enable_dnssec) except (exceptions.Error, calliope_exceptions.HttpException) as e: log.status.Print(six.text_type(e)) else: break return _CustomNameServers(name_servers, ds_records) elif index == 2: # Google Domains name servers. return _GoogleDomainsNameServers(enable_dnssec) else: return None, None # Cancel.