Merge pull request #276 from mesosphere/remove-ssh-agent
remove ssh-agent from `dcos node ssh`
This commit is contained in:
@@ -23,7 +23,6 @@ Options:
|
|||||||
--version Show version
|
--version Show version
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import dcoscli
|
import dcoscli
|
||||||
@@ -186,15 +185,7 @@ def _ssh(master, slave, option, config_file, user):
|
|||||||
:returns: process return code
|
:returns: process return code
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not os.environ.get('SSH_AUTH_SOCK'):
|
|
||||||
raise DCOSException(
|
|
||||||
"There is no SSH_AUTH_SOCK env variable, which likely means you " +
|
|
||||||
"aren't running `ssh-agent`. `dcos node ssh` depends on " +
|
|
||||||
"`ssh-agent` so we can safely use your private key to hop " +
|
|
||||||
"between nodes in your cluster. Please run `ssh-agent`, " +
|
|
||||||
"then add your private key with `ssh-add`.")
|
|
||||||
|
|
||||||
master_public_ip = mesos.DCOSClient().metadata()['PUBLIC_IPV4']
|
|
||||||
ssh_options = ' '.join('-o {}'.format(opt) for opt in option)
|
ssh_options = ' '.join('-o {}'.format(opt) for opt in option)
|
||||||
|
|
||||||
if config_file:
|
if config_file:
|
||||||
@@ -203,7 +194,7 @@ def _ssh(master, slave, option, config_file, user):
|
|||||||
ssh_config = ''
|
ssh_config = ''
|
||||||
|
|
||||||
if master:
|
if master:
|
||||||
host = 'leader.mesos'
|
host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip']
|
||||||
else:
|
else:
|
||||||
summary = mesos.DCOSClient().get_state_summary()
|
summary = mesos.DCOSClient().get_state_summary()
|
||||||
slave_obj = next((slave_ for slave_ in summary['slaves']
|
slave_obj = next((slave_ for slave_ in summary['slaves']
|
||||||
@@ -214,11 +205,10 @@ def _ssh(master, slave, option, config_file, user):
|
|||||||
else:
|
else:
|
||||||
raise DCOSException('No slave found with ID [{}]'.format(slave))
|
raise DCOSException('No slave found with ID [{}]'.format(slave))
|
||||||
|
|
||||||
cmd = "ssh -A -t {0} {1} {2}@{3} ssh -A -t {2}@{4}".format(
|
cmd = "ssh -t {0} {1} {2}@{3}".format(
|
||||||
ssh_options,
|
ssh_options,
|
||||||
ssh_config,
|
ssh_config,
|
||||||
user,
|
user,
|
||||||
master_public_ip,
|
|
||||||
host)
|
host)
|
||||||
|
|
||||||
return subprocess.call(cmd, shell=True)
|
return subprocess.call(cmd, shell=True)
|
||||||
|
|||||||
@@ -145,26 +145,14 @@ def test_node_ssh_user():
|
|||||||
assert stderr.startswith(b'Permission denied')
|
assert stderr.startswith(b'Permission denied')
|
||||||
|
|
||||||
|
|
||||||
def test_node_ssh_no_agent():
|
|
||||||
stderr = (b"There is no SSH_AUTH_SOCK env variable, which likely means "
|
|
||||||
b"you aren't running `ssh-agent`. `dcos node ssh` depends on"
|
|
||||||
b" `ssh-agent` so we can safely use your private key to hop "
|
|
||||||
b"between nodes in your cluster. Please run `ssh-agent`, then "
|
|
||||||
b"add your private key with `ssh-add`.\n")
|
|
||||||
assert_command(['dcos', 'node', 'ssh', '--master'],
|
|
||||||
stdout=b'',
|
|
||||||
stderr=stderr,
|
|
||||||
returncode=1)
|
|
||||||
|
|
||||||
|
|
||||||
def _node_ssh_output(args):
|
def _node_ssh_output(args):
|
||||||
# ssh must run with stdin attached to a tty
|
# ssh must run with stdin attached to a tty
|
||||||
master, slave = pty.openpty()
|
master, slave = pty.openpty()
|
||||||
|
cmd = ('dcos node ssh --option ' +
|
||||||
|
'IdentityFile=/host-home/.vagrant.d/insecure_private_key ' +
|
||||||
|
'--option StrictHostKeyChecking=no ' +
|
||||||
|
'{}').format(' '.join(args))
|
||||||
|
|
||||||
cmd = ('ssh-agent /bin/bash -c ' +
|
|
||||||
'"ssh-add /host-home/.vagrant.d/insecure_private_key ' +
|
|
||||||
'2> /dev/null && dcos node ssh --option StrictHostKeyChecking=no' +
|
|
||||||
' {}"').format(' '.join(args))
|
|
||||||
proc = subprocess.Popen(cmd,
|
proc = subprocess.Popen(cmd,
|
||||||
stdin=slave,
|
stdin=slave,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
|
|||||||
29
dcos/http.py
29
dcos/http.py
@@ -66,22 +66,21 @@ def request(method,
|
|||||||
:rtype: Response
|
:rtype: Response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if 'headers' not in kwargs:
|
||||||
|
kwargs['headers'] = {'Accept': 'application/json'}
|
||||||
|
|
||||||
|
request = requests.Request(
|
||||||
|
method=method,
|
||||||
|
url=url,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
'Sending HTTP [%r] to [%r]: %r',
|
||||||
|
request.method,
|
||||||
|
request.url,
|
||||||
|
request.headers)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if 'headers' in kwargs:
|
|
||||||
request = requests.Request(method=method, url=url, **kwargs)
|
|
||||||
else:
|
|
||||||
request = requests.Request(
|
|
||||||
method=method,
|
|
||||||
url=url,
|
|
||||||
headers={'Accept': 'application/json'},
|
|
||||||
**kwargs)
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
'Sending HTTP [%r] to [%r]: %r',
|
|
||||||
request.method,
|
|
||||||
request.url,
|
|
||||||
request.headers)
|
|
||||||
|
|
||||||
with requests.Session() as session:
|
with requests.Session() as session:
|
||||||
response = session.send(request.prepare(), timeout=timeout,
|
response = session.send(request.prepare(), timeout=timeout,
|
||||||
proxies=util.get_proxy_dict_from_env())
|
proxies=util.get_proxy_dict_from_env())
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def _get_marathon_url(config):
|
|||||||
|
|
||||||
marathon_url = config.get('marathon.url')
|
marathon_url = config.get('marathon.url')
|
||||||
if marathon_url is None:
|
if marathon_url is None:
|
||||||
dcos_url = util.get_config_vals(config, ['core.dcos_url'])[0]
|
dcos_url = util.get_config_vals(['core.dcos_url'], config)[0]
|
||||||
marathon_url = urllib.parse.urljoin(dcos_url, 'marathon/')
|
marathon_url = urllib.parse.urljoin(dcos_url, 'marathon/')
|
||||||
|
|
||||||
return marathon_url
|
return marathon_url
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class DCOSClient(object):
|
|||||||
|
|
||||||
mesos_master_url = config.get('core.mesos_master_url')
|
mesos_master_url = config.get('core.mesos_master_url')
|
||||||
if mesos_master_url is None:
|
if mesos_master_url is None:
|
||||||
self._dcos_url = util.get_config_vals(config, ['core.dcos_url'])[0]
|
self._dcos_url = util.get_config_vals(['core.dcos_url'], config)[0]
|
||||||
else:
|
else:
|
||||||
self._mesos_master_url = mesos_master_url
|
self._mesos_master_url = mesos_master_url
|
||||||
|
|
||||||
@@ -203,6 +203,38 @@ class DCOSClient(object):
|
|||||||
return http.get(url).json()
|
return http.get(url).json()
|
||||||
|
|
||||||
|
|
||||||
|
class MesosDNSClient(object):
|
||||||
|
""" Mesos-DNS client
|
||||||
|
|
||||||
|
:param url: mesos-dns URL
|
||||||
|
:type url: str
|
||||||
|
"""
|
||||||
|
def __init__(self, url=None):
|
||||||
|
self.url = url or urllib.parse.urljoin(
|
||||||
|
util.get_config_vals(['core.dcos_url'])[0], '/mesos_dns/')
|
||||||
|
|
||||||
|
def _path(self, path):
|
||||||
|
""" Construct a full path
|
||||||
|
|
||||||
|
:param path: path suffix
|
||||||
|
:type path: str
|
||||||
|
:returns: full path
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return urllib.parse.urljoin(self.url, path)
|
||||||
|
|
||||||
|
def hosts(self, host):
|
||||||
|
""" GET v1/hosts/<host>
|
||||||
|
|
||||||
|
:param host: host
|
||||||
|
:type host: str
|
||||||
|
:returns: {'ip', 'host'} dictionary
|
||||||
|
:rtype: dict(str, str)
|
||||||
|
"""
|
||||||
|
url = self._path('v1/hosts/{}'.format(host))
|
||||||
|
return http.get(url, headers={}).json()
|
||||||
|
|
||||||
|
|
||||||
class Master(object):
|
class Master(object):
|
||||||
"""Mesos Master Model
|
"""Mesos Master Model
|
||||||
|
|
||||||
|
|||||||
@@ -590,7 +590,7 @@ def list_sources(config):
|
|||||||
:rtype: [Source]
|
:rtype: [Source]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
source_uris = util.get_config_vals(config, ['package.sources'])[0]
|
source_uris = util.get_config_vals(['package.sources'], config)[0]
|
||||||
|
|
||||||
sources = [url_to_source(s) for s in source_uris]
|
sources = [url_to_source(s) for s in source_uris]
|
||||||
|
|
||||||
@@ -659,7 +659,7 @@ def update_sources(config, validate=False):
|
|||||||
|
|
||||||
# ensure the cache directory is properly configured
|
# ensure the cache directory is properly configured
|
||||||
cache_dir = os.path.expanduser(
|
cache_dir = os.path.expanduser(
|
||||||
util.get_config_vals(config, ['package.cache'])[0])
|
util.get_config_vals(['package.cache'], config)[0])
|
||||||
|
|
||||||
# ensure the cache directory exists
|
# ensure the cache directory exists
|
||||||
if not os.path.exists(cache_dir):
|
if not os.path.exists(cache_dir):
|
||||||
@@ -758,7 +758,7 @@ class Source:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
cache_dir = os.path.expanduser(
|
cache_dir = os.path.expanduser(
|
||||||
util.get_config_vals(config, ['package.cache'])[0])
|
util.get_config_vals(['package.cache'], config)[0])
|
||||||
return os.path.join(cache_dir, self.hash())
|
return os.path.join(cache_dir, self.hash())
|
||||||
|
|
||||||
def copy_to_cache(self, target_dir):
|
def copy_to_cache(self, target_dir):
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ def get_config():
|
|||||||
os.environ[constants.DCOS_CONFIG_ENV])
|
os.environ[constants.DCOS_CONFIG_ENV])
|
||||||
|
|
||||||
|
|
||||||
def get_config_vals(config, keys):
|
def get_config_vals(keys, config=None):
|
||||||
"""Gets config values for each of the keys. Raises a DCOSException if
|
"""Gets config values for each of the keys. Raises a DCOSException if
|
||||||
any of the keys don't exist.
|
any of the keys don't exist.
|
||||||
|
|
||||||
@@ -127,6 +127,7 @@ def get_config_vals(config, keys):
|
|||||||
:rtype: [object]
|
:rtype: [object]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
config = config or get_config()
|
||||||
missing = [key for key in keys if key not in config]
|
missing = [key for key in keys if key not in config]
|
||||||
if missing:
|
if missing:
|
||||||
raise missing_config_exception(keys)
|
raise missing_config_exception(keys)
|
||||||
|
|||||||
Reference in New Issue
Block a user