log: use endpoint to determine logs source (#912)
DC/OS metadata endpoint will expose a config where the user is able to specify what logging strategy to use. We should be using `dcos-log` API only if user explicitly disables sandbox logging. if user enabled journald, we can use it to read system logs. https://github.com/dcos/dcos/pull/1259
This commit is contained in:
		@@ -6,10 +6,14 @@ import sys
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import six
 | 
			
		||||
from six.moves import urllib
 | 
			
		||||
 | 
			
		||||
from dcos import emitting, http, packagemanager, sse, util
 | 
			
		||||
from dcos import config, emitting, http, packagemanager, sse, util
 | 
			
		||||
from dcos.cosmos import get_cosmos_url
 | 
			
		||||
from dcos.errors import DCOSException, DefaultError
 | 
			
		||||
from dcos.errors import (DCOSAuthenticationException,
 | 
			
		||||
                         DCOSAuthorizationException,
 | 
			
		||||
                         DCOSException,
 | 
			
		||||
                         DefaultError)
 | 
			
		||||
 | 
			
		||||
logger = util.get_logger(__name__)
 | 
			
		||||
emitter = emitting.FlatEmitter()
 | 
			
		||||
@@ -214,9 +218,48 @@ def dcos_log_enabled():
 | 
			
		||||
    :return: does cosmos have LOGGING capability.
 | 
			
		||||
    :rtype: bool
 | 
			
		||||
    """
 | 
			
		||||
    return packagemanager.PackageManager(
 | 
			
		||||
 | 
			
		||||
    # https://github.com/dcos/dcos/blob/master/gen/calc.py#L151
 | 
			
		||||
    return logging_strategy() == 'journald'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def logging_strategy():
 | 
			
		||||
    """ function returns logging strategy
 | 
			
		||||
 | 
			
		||||
    :return: does cosmos have LOGGING capability.
 | 
			
		||||
    :rtype: str
 | 
			
		||||
    """
 | 
			
		||||
    # default strategy is sandbox logging.
 | 
			
		||||
    strategy = 'logrotate'
 | 
			
		||||
 | 
			
		||||
    has_capability = packagemanager.PackageManager(
 | 
			
		||||
        get_cosmos_url()).has_capability('LOGGING')
 | 
			
		||||
 | 
			
		||||
    if not has_capability:
 | 
			
		||||
        return strategy
 | 
			
		||||
 | 
			
		||||
    base_url = config.get_config_val("core.dcos_url")
 | 
			
		||||
    url = urllib.parse.urljoin(base_url, '/dcos-metadata/ui-config.json')
 | 
			
		||||
 | 
			
		||||
    if not base_url:
 | 
			
		||||
        raise config.missing_config_exception(['core.dcos_url'])
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        response = http.get(url).json()
 | 
			
		||||
    except (DCOSAuthenticationException, DCOSAuthorizationException):
 | 
			
		||||
        raise
 | 
			
		||||
    except DCOSException:
 | 
			
		||||
        emitter.publish('Unable to determine logging mechanism for '
 | 
			
		||||
                        'your cluster. Defaulting to files API.')
 | 
			
		||||
        return strategy
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        strategy = response['uiConfiguration']['plugins']['mesos']['logging-strategy']  # noqa: ignore=F403,E501
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    return strategy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def follow_logs(url):
 | 
			
		||||
    """ Function will use dcos.sse.get to subscribe to server sent events
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,7 @@ import dcoscli
 | 
			
		||||
from dcos import (cmds, config, emitting, errors,
 | 
			
		||||
                  http, mesos, packagemanager, subprocess, util)
 | 
			
		||||
from dcos.cosmos import get_cosmos_url
 | 
			
		||||
from dcos.errors import (DCOSAuthenticationException,
 | 
			
		||||
                         DCOSAuthorizationException,
 | 
			
		||||
                         DCOSException, DefaultError)
 | 
			
		||||
from dcos.errors import DCOSException, DefaultError
 | 
			
		||||
from dcoscli import log, tables
 | 
			
		||||
from dcoscli.package.main import confirm
 | 
			
		||||
from dcoscli.subcommand import default_command_info, default_doc
 | 
			
		||||
@@ -492,25 +490,17 @@ def _log(follow, lines, leader, slave, component, filters):
 | 
			
		||||
    :rtype: int
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    if not (leader or slave) or (leader and slave):
 | 
			
		||||
        raise DCOSException(
 | 
			
		||||
            'You must choose one of --leader or --mesos-id.')
 | 
			
		||||
    if not (leader or slave):
 | 
			
		||||
        raise DCOSException('You must choose one of --leader or --mesos-id.')
 | 
			
		||||
 | 
			
		||||
    if lines is None:
 | 
			
		||||
        lines = 10
 | 
			
		||||
 | 
			
		||||
    lines = util.parse_int(lines)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        _dcos_log(follow, lines, leader, slave, component, filters)
 | 
			
		||||
        return 0
 | 
			
		||||
    except (DCOSAuthenticationException,
 | 
			
		||||
            DCOSAuthorizationException):
 | 
			
		||||
            raise
 | 
			
		||||
    except DCOSException as e:
 | 
			
		||||
        emitter.publish(DefaultError(e))
 | 
			
		||||
        emitter.publish(DefaultError('Falling back to files API...'))
 | 
			
		||||
 | 
			
		||||
    # if journald logging is disabled. Read from files API and exit.
 | 
			
		||||
    # https://github.com/dcos/dcos/blob/master/gen/calc.py#L151
 | 
			
		||||
    if 'journald' not in log.logging_strategy():
 | 
			
		||||
        if component or filters:
 | 
			
		||||
            raise DCOSException('--component or --filter is not '
 | 
			
		||||
                                'supported by files API')
 | 
			
		||||
@@ -520,6 +510,15 @@ def _log(follow, lines, leader, slave, component, filters):
 | 
			
		||||
        log.log_files(mesos_files, follow, lines)
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    # dcos-log does not support logs from leader and agent.
 | 
			
		||||
    if leader and slave:
 | 
			
		||||
        raise DCOSException(
 | 
			
		||||
            'You must choose one of --leader or --mesos-id.')
 | 
			
		||||
 | 
			
		||||
    # if journald logging enabled.
 | 
			
		||||
    _dcos_log(follow, lines, leader, slave, component, filters)
 | 
			
		||||
    return 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_slave_ip(slave):
 | 
			
		||||
    """ Get an agent IP address based on mesos id.
 | 
			
		||||
@@ -678,8 +677,6 @@ def _dcos_log(follow, lines, leader, slave, component, filters):
 | 
			
		||||
    :param filters: a list of filters ["key:value", ...]
 | 
			
		||||
    :type filters: list
 | 
			
		||||
    """
 | 
			
		||||
    if not log.dcos_log_enabled():
 | 
			
		||||
        raise DCOSException('dcos-log is not supported')
 | 
			
		||||
 | 
			
		||||
    filter_query = ''
 | 
			
		||||
    if component:
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,7 @@ import six
 | 
			
		||||
 | 
			
		||||
import dcoscli
 | 
			
		||||
from dcos import cmds, emitting, marathon, mesos, subprocess, util
 | 
			
		||||
from dcos.errors import (DCOSAuthenticationException,
 | 
			
		||||
                         DCOSAuthorizationException,
 | 
			
		||||
                         DCOSException,
 | 
			
		||||
                         DefaultError)
 | 
			
		||||
from dcos.errors import DCOSException, DefaultError
 | 
			
		||||
from dcoscli import log, tables
 | 
			
		||||
from dcoscli.subcommand import default_command_info, default_doc
 | 
			
		||||
from dcoscli.util import decorate_docopt_usage
 | 
			
		||||
@@ -175,22 +172,17 @@ def _log_service(follow, lines, service, file_):
 | 
			
		||||
        file_ = 'stdout'
 | 
			
		||||
 | 
			
		||||
    task = _get_service_task(service)
 | 
			
		||||
    try:
 | 
			
		||||
 | 
			
		||||
    # if journald logging is disabled, read from files API.
 | 
			
		||||
    if not log.dcos_log_enabled():
 | 
			
		||||
        return _log_task(task['id'], follow, lines, file_)
 | 
			
		||||
 | 
			
		||||
    if 'id' not in task:
 | 
			
		||||
        raise DCOSException('Missing `id` in task. {}'.format(task))
 | 
			
		||||
 | 
			
		||||
    task_id = task['id']
 | 
			
		||||
    task_main._log(follow, False, lines, task_id, file_)
 | 
			
		||||
    return 0
 | 
			
		||||
    except (DCOSAuthenticationException,
 | 
			
		||||
            DCOSAuthorizationException):
 | 
			
		||||
        raise
 | 
			
		||||
    except DCOSException as e:
 | 
			
		||||
        emitter.publish(DefaultError(e))
 | 
			
		||||
        emitter.publish(DefaultError('Falling back to files API...'))
 | 
			
		||||
 | 
			
		||||
    task = _get_service_task(service)
 | 
			
		||||
    return _log_task(task['id'], follow, lines, file_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _log_task(task_id, follow, lines, file_):
 | 
			
		||||
 
 | 
			
		||||
@@ -7,9 +7,7 @@ import six
 | 
			
		||||
 | 
			
		||||
import dcoscli
 | 
			
		||||
from dcos import cmds, config, emitting, mesos, util
 | 
			
		||||
from dcos.errors import (DCOSAuthenticationException,
 | 
			
		||||
                         DCOSAuthorizationException,
 | 
			
		||||
                         DCOSException, DCOSHTTPException, DefaultError)
 | 
			
		||||
from dcos.errors import DCOSException, DCOSHTTPException, DefaultError
 | 
			
		||||
from dcoscli import log, tables
 | 
			
		||||
from dcoscli.subcommand import default_command_info, default_doc
 | 
			
		||||
from dcoscli.util import decorate_docopt_usage
 | 
			
		||||
@@ -232,17 +230,8 @@ def _log(follow, completed, lines, task, file_):
 | 
			
		||||
                raise DCOSException(msg)
 | 
			
		||||
        raise DCOSException('No matching tasks. Exiting.')
 | 
			
		||||
 | 
			
		||||
    if file_ in ('stdout', 'stderr') and log.dcos_log_enabled():
 | 
			
		||||
        try:
 | 
			
		||||
            _dcos_log(follow, tasks, lines, file_, completed)
 | 
			
		||||
            return 0
 | 
			
		||||
        except (DCOSAuthenticationException,
 | 
			
		||||
                DCOSAuthorizationException):
 | 
			
		||||
            raise
 | 
			
		||||
        except DCOSException as e:
 | 
			
		||||
            emitter.publish(DefaultError(e))
 | 
			
		||||
            emitter.publish(DefaultError('Falling back to files API...'))
 | 
			
		||||
 | 
			
		||||
    # if journald logging is disabled, read files API and exit.
 | 
			
		||||
    if not log.dcos_log_enabled():
 | 
			
		||||
        mesos_files = _mesos_files(tasks, file_, client)
 | 
			
		||||
        if not mesos_files:
 | 
			
		||||
            if fltr is None:
 | 
			
		||||
@@ -252,9 +241,17 @@ def _log(follow, completed, lines, task, file_):
 | 
			
		||||
            raise DCOSException(msg)
 | 
			
		||||
 | 
			
		||||
        log.log_files(mesos_files, follow, lines)
 | 
			
		||||
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    # otherwise
 | 
			
		||||
    if file_ in ('stdout', 'stderr'):
 | 
			
		||||
        _dcos_log(follow, tasks, lines, file_, completed)
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    raise DCOSException('Invalid file {}. dcos-log only '
 | 
			
		||||
                        'supports stdout/stderr'.format(file_))
 | 
			
		||||
    return 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_nested_container_id(task):
 | 
			
		||||
    """ Get the nested container id from mesos state.
 | 
			
		||||
 
 | 
			
		||||
@@ -68,8 +68,7 @@ def test_node_log_missing_slave():
 | 
			
		||||
    assert returncode == 1
 | 
			
		||||
    assert stdout == b''
 | 
			
		||||
    stderr_str = str(stderr)
 | 
			
		||||
    assert 'HTTP 404' in stderr_str
 | 
			
		||||
    assert 'No slave found with ID "bogus".' in stderr_str
 | 
			
		||||
    assert 'HTTP 404: Not Found' in stderr_str
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_node_log_lines():
 | 
			
		||||
 
 | 
			
		||||
@@ -188,7 +188,7 @@ def test_log_two_tasks():
 | 
			
		||||
    assert stderr == b''
 | 
			
		||||
 | 
			
		||||
    lines = stdout.decode('utf-8').split('\n')
 | 
			
		||||
    assert len(lines) == 11
 | 
			
		||||
    assert len(lines) == 23
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.skipif(sys.platform == 'win32',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user