File: //snap/google-cloud-cli/current/lib/googlecloudsdk/command_lib/interactive/browser.py
# -*- 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))