211 lines
6.0 KiB
Python
211 lines
6.0 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2017 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.
|
|
|
|
"""Tools for launching a browser."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import abc
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import webbrowser
|
|
|
|
from googlecloudsdk.command_lib.interactive import parser
|
|
from googlecloudsdk.core import log
|
|
from googlecloudsdk.core.util import encoding
|
|
from googlecloudsdk.core.util import files
|
|
import six
|
|
|
|
|
|
class FakeSubprocessModule(object):
|
|
|
|
def Popen(self, args, **kwargs):
|
|
with files.FileWriter(os.devnull) as devnull:
|
|
kwargs.update({'stderr': devnull, 'stdout': devnull})
|
|
return subprocess.Popen(args, **kwargs)
|
|
|
|
|
|
class CommandReferenceMapper(six.with_metaclass(abc.ABCMeta, object)):
|
|
"""Command to URL or man page reference mapper base class."""
|
|
|
|
def __init__(self, cli, args):
|
|
self.cli = cli
|
|
self.args = args
|
|
|
|
@abc.abstractmethod
|
|
def GetMan(self):
|
|
"""Returns the man-style command for the command in args."""
|
|
return None
|
|
|
|
@abc.abstractmethod
|
|
def GetURL(self):
|
|
"""Returns the help doc URL for the command in args."""
|
|
return None
|
|
|
|
|
|
class GcloudReferenceMapper(CommandReferenceMapper):
|
|
"""gcloud help reference mapper."""
|
|
|
|
def GetMan(self):
|
|
return ' '.join(self.args + ['--help'])
|
|
|
|
def GetURL(self):
|
|
return '/'.join(
|
|
['https://cloud.google.com/sdk/gcloud/reference'] + self.args[1:])
|
|
|
|
|
|
class BqReferenceMapper(CommandReferenceMapper):
|
|
"""bq help reference mapper."""
|
|
|
|
def GetMan(self):
|
|
return self.args[0] + ' help | less'
|
|
|
|
def GetURL(self):
|
|
return 'https://cloud.google.com/bigquery/bq-command-line-tool'
|
|
|
|
|
|
class GsutilReferenceMapper(CommandReferenceMapper):
|
|
"""gsutil help reference mapper."""
|
|
|
|
def __init__(self, cli, args):
|
|
super(GsutilReferenceMapper, self).__init__(cli, args)
|
|
self.subcommand = args[1] if len(args) > 1 else ''
|
|
self.ref = ['https://cloud.google.com/storage/docs/gsutil']
|
|
|
|
def GetMan(self):
|
|
cmd = ['gsutil help']
|
|
if self.subcommand:
|
|
cmd.append(self.subcommand)
|
|
cmd.append('| less')
|
|
return ' '.join(cmd)
|
|
|
|
def GetURL(self):
|
|
if self.subcommand:
|
|
self.ref.append('commands')
|
|
self.ref.append(self.subcommand)
|
|
return '/'.join(self.ref)
|
|
|
|
|
|
class KubectlReferenceMapper(CommandReferenceMapper):
|
|
"""kubectl help reference mapper."""
|
|
|
|
def __init__(self, cli, args):
|
|
super(KubectlReferenceMapper, self).__init__(cli, args)
|
|
self.subcommand = args[1] if len(args) > 1 else ''
|
|
try:
|
|
full_version = (cli.root[parser.LOOKUP_COMMANDS][args[0]]
|
|
[parser.LOOKUP_CLI_VERSION])
|
|
version = '.'.join(full_version.split('.')[0:2])
|
|
except (IndexError, KeyError):
|
|
version = 'v1.8'
|
|
self.ref = ['https://kubernetes.io/docs/user-guide/kubectl', version]
|
|
|
|
def GetMan(self):
|
|
cmd = ['kubectl help']
|
|
if self.subcommand:
|
|
cmd.append(self.subcommand)
|
|
cmd.append('| less')
|
|
return ' '.join(cmd)
|
|
|
|
def GetURL(self):
|
|
if self.subcommand:
|
|
self.ref.append('#' + self.subcommand)
|
|
return '/'.join(self.ref)
|
|
|
|
|
|
class UnknownReferenceMapper(CommandReferenceMapper):
|
|
"""Unkmown command help reference mapper."""
|
|
|
|
def __init__(self, cli, args):
|
|
super(UnknownReferenceMapper, self).__init__(cli, args)
|
|
self.known = files.FindExecutableOnPath(args[0])
|
|
|
|
def GetMan(self):
|
|
if not self.known:
|
|
return None
|
|
return 'man ' + self.args[0]
|
|
|
|
def GetURL(self):
|
|
if not self.known:
|
|
return None
|
|
if 'darwin' in sys.platform:
|
|
ref = ['https://developer.apple.com/legacy/library/documentation',
|
|
'Darwin/Reference/ManPages/man1']
|
|
else:
|
|
ref = ['http://man7.org/linux/man-pages/man1']
|
|
ref.append(self.args[0] + '.1.html')
|
|
return '/'.join(ref)
|
|
|
|
|
|
def _GetReferenceURL(cli, line, pos=None, man_page=False):
|
|
"""Determine the reference url of the command/group preceding the pos.
|
|
|
|
Args:
|
|
cli: the prompt CLI object
|
|
line: a string with the current string directly from the shell.
|
|
pos: the position of the cursor on the line.
|
|
man_page: Return help/man page command line if True.
|
|
|
|
Returns:
|
|
A string containing the URL of the reference page.
|
|
"""
|
|
mappers = {
|
|
'bq': BqReferenceMapper,
|
|
'gcloud': GcloudReferenceMapper,
|
|
'gsutil': GsutilReferenceMapper,
|
|
'kubectl': KubectlReferenceMapper,
|
|
}
|
|
|
|
if pos is None:
|
|
pos = len(line)
|
|
args = []
|
|
for arg in cli.parser.ParseCommand(line):
|
|
if arg.start < pos and (
|
|
not args or
|
|
arg.tree.get(parser.LOOKUP_COMMANDS) or # IS_GROUP workaround
|
|
arg.token_type in (parser.ArgTokenType.COMMAND,
|
|
parser.ArgTokenType.GROUP)):
|
|
args.append(arg.value)
|
|
if not args:
|
|
if line:
|
|
return None
|
|
args = ['gcloud', 'alpha', 'interactive']
|
|
|
|
mapper_class = mappers.get(args[0], UnknownReferenceMapper)
|
|
mapper = mapper_class(cli, args)
|
|
return mapper.GetMan() if man_page else mapper.GetURL()
|
|
|
|
|
|
def OpenReferencePage(cli, line, pos):
|
|
"""Opens a web browser or local help/man page for line at pos."""
|
|
man_page = bool(encoding.GetEncodedValue(os.environ, 'SSH_CLIENT'))
|
|
ref = _GetReferenceURL(cli, line, pos, man_page)
|
|
if not ref:
|
|
return
|
|
if man_page:
|
|
cli.Run(ref, alternate_screen=True)
|
|
return
|
|
|
|
webbrowser.subprocess = FakeSubprocessModule()
|
|
try:
|
|
browser = webbrowser.get()
|
|
browser.open_new_tab(ref)
|
|
except webbrowser.Error as e:
|
|
cli.run_in_terminal(
|
|
lambda: log.error('failed to open browser: %s', e))
|