diff --git a/cli/dcoscli/node/main.py b/cli/dcoscli/node/main.py index ce1ef35..7fd8fd3 100644 --- a/cli/dcoscli/node/main.py +++ b/cli/dcoscli/node/main.py @@ -23,7 +23,6 @@ Options: --version Show version """ -import os import subprocess import dcoscli @@ -186,15 +185,7 @@ def _ssh(master, slave, option, config_file, user): :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) if config_file: @@ -203,7 +194,7 @@ def _ssh(master, slave, option, config_file, user): ssh_config = '' if master: - host = 'leader.mesos' + host = mesos.MesosDNSClient().hosts('leader.mesos.')[0]['ip'] else: summary = mesos.DCOSClient().get_state_summary() slave_obj = next((slave_ for slave_ in summary['slaves'] @@ -214,11 +205,10 @@ def _ssh(master, slave, option, config_file, user): else: 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_config, user, - master_public_ip, host) return subprocess.call(cmd, shell=True) diff --git a/cli/tests/integrations/test_node.py b/cli/tests/integrations/test_node.py index 6f15852..191dc7d 100644 --- a/cli/tests/integrations/test_node.py +++ b/cli/tests/integrations/test_node.py @@ -145,26 +145,14 @@ def test_node_ssh_user(): 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): # ssh must run with stdin attached to a tty 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, stdin=slave, stdout=subprocess.PIPE, diff --git a/dcos/http.py b/dcos/http.py index 1ffa206..99dd2b2 100644 --- a/dcos/http.py +++ b/dcos/http.py @@ -66,22 +66,21 @@ def request(method, :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: - 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: response = session.send(request.prepare(), timeout=timeout, proxies=util.get_proxy_dict_from_env()) diff --git a/dcos/marathon.py b/dcos/marathon.py index a96acd9..7e3ec4e 100644 --- a/dcos/marathon.py +++ b/dcos/marathon.py @@ -37,7 +37,7 @@ def _get_marathon_url(config): marathon_url = config.get('marathon.url') 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/') return marathon_url diff --git a/dcos/mesos.py b/dcos/mesos.py index 58a7ddf..c610a0f 100644 --- a/dcos/mesos.py +++ b/dcos/mesos.py @@ -36,7 +36,7 @@ class DCOSClient(object): mesos_master_url = config.get('core.mesos_master_url') 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: self._mesos_master_url = mesos_master_url @@ -203,6 +203,38 @@ class DCOSClient(object): 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/ + + :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): """Mesos Master Model diff --git a/dcos/package.py b/dcos/package.py index a456e7b..b9e356b 100644 --- a/dcos/package.py +++ b/dcos/package.py @@ -590,7 +590,7 @@ def list_sources(config): :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] @@ -659,7 +659,7 @@ def update_sources(config, validate=False): # ensure the cache directory is properly configured 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 if not os.path.exists(cache_dir): @@ -758,7 +758,7 @@ class Source: """ 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()) def copy_to_cache(self, target_dir): diff --git a/dcos/util.py b/dcos/util.py index 23009c8..df01478 100644 --- a/dcos/util.py +++ b/dcos/util.py @@ -115,7 +115,7 @@ def get_config(): 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 any of the keys don't exist. @@ -127,6 +127,7 @@ def get_config_vals(config, keys): :rtype: [object] """ + config = config or get_config() missing = [key for key in keys if key not in config] if missing: raise missing_config_exception(keys)