Commit 1d1bcf8b authored by Xavier Thompson's avatar Xavier Thompson

slapgrid: Fix promises not being logged to logfile

See merge request nexedi/slapos.core!356
parents 3c0c7a03 c879dd93
...@@ -60,11 +60,11 @@ promise_checker = PromiseLauncher(config=config, logger=app.log) ...@@ -60,11 +60,11 @@ promise_checker = PromiseLauncher(config=config, logger=app.log)
# Run promises # Run promises
# Redirect stdout to stderr (logger only uses stderr already) # Redirect stderr to stdout (logger uses stderr)
# to reserve stdout for error reporting # to reserve stderr exclusively for error reporting
out = os.dup(1) err = os.dup(2)
os.dup2(2, 1) os.dup2(1, 2)
try: try:
promise_checker.run() promise_checker.run()
...@@ -73,5 +73,5 @@ except Exception as e: ...@@ -73,5 +73,5 @@ except Exception as e:
error_str = unicode(str(e), 'utf-8', 'repr') error_str = unicode(str(e), 'utf-8', 'repr')
else: else:
error_str = str(e) error_str = str(e)
os.write(out, error_str.encode('utf-8', 'repr')) os.write(err, error_str.encode('utf-8', 'repr'))
sys.exit(2 if isinstance(e, PromiseError) else 1) sys.exit(2 if isinstance(e, PromiseError) else 1)
...@@ -726,26 +726,30 @@ stderr_logfile_backups=1 ...@@ -726,26 +726,30 @@ stderr_logfile_backups=1
else: else:
command.append('--' + option) command.append('--' + option)
command.append(str(value)) command.append(str(value))
process = subprocess.Popen(
command,
preexec_fn=lambda: dropPrivileges(uid, gid, logger=self.logger),
cwd=instance_path,
universal_newlines=True,
stdout=subprocess.PIPE)
promises = plugins + len(listifdir(legacy_promise_dir)) promises = plugins + len(listifdir(legacy_promise_dir))
# Add a timeout margin to let the process kill the promises and cleanup # Add a timeout margin to let the process kill the promises and cleanup
timeout = promises * self.promise_timeout + 10 timeout = promises * self.promise_timeout + 10
# The runpromise script uses stderr exclusively to propagate exception
# messages. It otherwise redirects stderr to stdout so that all outputs
# from the promises go to stdout.
try: try:
# The logger logs everything to stderr, so runpromise redirects process = SlapPopen(
# stdout to stderr in case a promise prints to stdout command,
# and reserves stdout to progagate exception messages. preexec_fn=lambda: dropPrivileges(uid, gid, logger=self.logger),
out, _ = process.communicate(timeout=timeout) cwd=instance_path,
universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
logger=self.logger,
timeout=timeout,
)
stderr = process.stderr.read()
if process.returncode == 2: if process.returncode == 2:
raise PromiseError(out) raise PromiseError(stderr)
elif process.returncode: elif process.returncode:
raise Exception(out) raise Exception(stderr)
elif out: elif stderr:
self.logger.warn('Promise runner unexpected output:\n%s', out) self.logger.warn('Promise runner unexpected output:\n%s', stderr)
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
killProcessTree(process.pid, self.logger) killProcessTree(process.pid, self.logger)
# The timeout margin was exceeded but this should be infrequent # The timeout margin was exceeded but this should be infrequent
......
...@@ -34,13 +34,17 @@ import os ...@@ -34,13 +34,17 @@ import os
import pkg_resources import pkg_resources
import pwd import pwd
import stat import stat
import subprocess
import sys import sys
import threading import threading
import logging import logging
import psutil import psutil
import time import time
if sys.version_info >= (3,):
import subprocess
else:
import subprocess32 as subprocess
from slapos.grid.exception import BuildoutFailedError, WrongPermissionError from slapos.grid.exception import BuildoutFailedError, WrongPermissionError
...@@ -119,6 +123,7 @@ class SlapPopen(subprocess.Popen): ...@@ -119,6 +123,7 @@ class SlapPopen(subprocess.Popen):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
logger = kwargs.pop('logger') logger = kwargs.pop('logger')
timeout = kwargs.pop('timeout', None)
debug = kwargs.pop('debug', False) debug = kwargs.pop('debug', False)
if debug: if debug:
...@@ -154,7 +159,7 @@ class SlapPopen(subprocess.Popen): ...@@ -154,7 +159,7 @@ class SlapPopen(subprocess.Popen):
args=(self.stdout, output_lines, logger)) args=(self.stdout, output_lines, logger))
t.start() t.start()
try: try:
self.wait() self.wait(timeout=timeout)
finally: finally:
t.join() t.join()
self.output = ''.join(output_lines) self.output = ''.join(output_lines)
......
...@@ -4214,6 +4214,32 @@ class TestSlapgridPluginPromiseWithInstancePython(TestSlapgridPromiseWithMaster) ...@@ -4214,6 +4214,32 @@ class TestSlapgridPluginPromiseWithInstancePython(TestSlapgridPromiseWithMaster)
dummyLogger.mock_calls[-1][1][0] % dummyLogger.mock_calls[-1][1][1:], dummyLogger.mock_calls[-1][1][0] % dummyLogger.mock_calls[-1][1][1:],
" 0[(not ready)]: Promise 'failing_promise_plugin.py' failed with output: héhé fake promise plugin error") " 0[(not ready)]: Promise 'failing_promise_plugin.py' failed with output: héhé fake promise plugin error")
def test_succeeding_promise_logs_output(self):
computer = self.getTestComputerClass()(self.software_root, self.instance_root, 1, 1)
instance, = computer.instance_list
name = "succeeding_promise_plugin.py"
output = "hehe fake promise plugin succeded !"
instance.requested_state = 'started'
instance.setPluginPromise(
name,
promise_content="""if 1:
return self.logger.info(%r)
""" % output,
)
with httmock.HTTMock(computer.request_handler), \
patch.object(self.grid.logger, 'info',) as dummyLogger:
self.launchSlapgrid()
self.assertIn(
"Checking promise %s..." % name,
dummyLogger.mock_calls[-4][1][0] % dummyLogger.mock_calls[-4][1][1:])
self.assertIn(
output,
dummyLogger.mock_calls[-3][1][0] % dummyLogger.mock_calls[-3][1][1:])
class TestSlapgridPluginPromiseWithInstancePythonOldSlapOSCompatibility( class TestSlapgridPluginPromiseWithInstancePythonOldSlapOSCompatibility(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment