Merge lp:~tealeg/landscape-client/notify-on-script-output-truncation into lp:~landscape/landscape-client/trunk

Proposed by Geoff Teale
Status: Merged
Approved by: Geoff Teale
Approved revision: 927
Merged at revision: 923
Proposed branch: lp:~tealeg/landscape-client/notify-on-script-output-truncation
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 125 lines (+54/-11)
2 files modified
landscape/manager/scriptexecution.py (+19/-7)
landscape/manager/tests/test_scriptexecution.py (+35/-4)
To merge this branch: bzr merge lp:~tealeg/landscape-client/notify-on-script-output-truncation
Reviewer Review Type Date Requested Status
🤖 Landscape Builder test results Approve
Adam Collard (community) Approve
Alberto Donato (community) Approve
Review via email: mp+307215@code.launchpad.net

Commit message

Add an indication of truncation to process output that has been truncated prior to delivery to the server.

Description of the change

Add an indication of truncation to process output that has been truncated prior to delivery to the server.

Testing instructions:

Run the test suite ;-)

To post a comment you must log in.
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Fail
Revno: 923
Branch: lp:~tealeg/landscape-client/notify-on-script-output-truncation
Jenkins: https://ci.lscape.net/job/latch-test-precise/126/

review: Needs Fixing (test results)
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 924
Branch: lp:~tealeg/landscape-client/notify-on-script-output-truncation
Jenkins: https://ci.lscape.net/job/latch-test-precise/127/

review: Approve (test results)
Revision history for this message
Alberto Donato (ack) wrote :

+1 looks good.

Just a nit inline.

review: Approve
Revision history for this message
Adam Collard (adam-collard) :
review: Needs Fixing
Revision history for this message
Geoff Teale (tealeg) wrote :

All points addressed.

Revision history for this message
Adam Collard (adam-collard) wrote :

Nice, thanks for that. +1

review: Approve
Revision history for this message
🤖 Landscape Builder (landscape-builder) :
review: Abstain (executing tests)
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :

Command: TRIAL_ARGS=-j4 make check
Result: Success
Revno: 926
Branch: lp:~tealeg/landscape-client/notify-on-script-output-truncation
Jenkins: https://ci.lscape.net/job/latch-test-precise/158/

review: Approve (test results)
927. By Geoff Teale

lint

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'landscape/manager/scriptexecution.py'
--- landscape/manager/scriptexecution.py 2016-08-17 20:31:48 +0000
+++ landscape/manager/scriptexecution.py 2016-10-06 11:03:00 +0000
@@ -6,7 +6,6 @@
6import os6import os
7import pwd7import pwd
8import tempfile8import tempfile
9import operator
10import shutil9import shutil
1110
12from twisted.internet.protocol import ProcessProtocol11from twisted.internet.protocol import ProcessProtocol
@@ -95,6 +94,8 @@
95 process with.94 process with.
96 """95 """
9796
97 truncation_indicator = "\n**OUTPUT TRUNCATED**"
98
98 def __init__(self, process_factory=None):99 def __init__(self, process_factory=None):
99 if process_factory is None:100 if process_factory is None:
100 from twisted.internet import reactor as process_factory101 from twisted.internet import reactor as process_factory
@@ -124,7 +125,7 @@
124 gid = None125 gid = None
125126
126 pp = ProcessAccumulationProtocol(127 pp = ProcessAccumulationProtocol(
127 self.registry.reactor, self.size_limit)128 self.registry.reactor, self.size_limit, self.truncation_indicator)
128 self.process_factory.spawnProcess(129 self.process_factory.spawnProcess(
129 pp, filename, uid=uid, gid=gid, path=path, env=env)130 pp, filename, uid=uid, gid=gid, path=path, env=env)
130 if time_limit is not None:131 if time_limit is not None:
@@ -309,11 +310,15 @@
309 @ivar size_limit: The number of bytes at which to truncate output.310 @ivar size_limit: The number of bytes at which to truncate output.
310 """311 """
311312
312 def __init__(self, reactor, size_limit):313 def __init__(self, reactor, size_limit, truncation_indicator=""):
313 self.data = []314 self.data = []
315 self._size = 0
314 self.result_deferred = Deferred()316 self.result_deferred = Deferred()
315 self._cancelled = False317 self._cancelled = False
316 self.size_limit = size_limit318 self.size_limit = size_limit
319 self._truncation_indicator = truncation_indicator
320 self._truncation_offset = len(self._truncation_indicator)
321 self._truncated_size_limit = self.size_limit - self._truncation_offset
317 self.reactor = reactor322 self.reactor = reactor
318 self._scheduled_cancel = None323 self._scheduled_cancel = None
319324
@@ -327,8 +332,15 @@
327 Add it to our buffer, as long as it doesn't go over L{size_limit}332 Add it to our buffer, as long as it doesn't go over L{size_limit}
328 bytes.333 bytes.
329 """334 """
330 current_size = reduce(operator.add, map(len, self.data), 0)335 if self._size < self.size_limit:
331 self.data.append(data[:self.size_limit - current_size])336 data_length = len(data)
337 if (self._size + data_length) >= self._truncated_size_limit:
338 extent = (self._truncated_size_limit - self._size)
339 self.data.append(data[:extent] + self._truncation_indicator)
340 self._size = self.size_limit
341 else:
342 self.data.append(data)
343 self._size += data_length
332344
333 def processEnded(self, reason):345 def processEnded(self, reason):
334 """Fire back the deferred.346 """Fire back the deferred.
@@ -351,8 +363,8 @@
351 if reason.check(ProcessDone):363 if reason.check(ProcessDone):
352 self.result_deferred.callback(data)364 self.result_deferred.callback(data)
353 else:365 else:
354 self.result_deferred.errback(ProcessFailedError(data,366 self.result_deferred.errback(
355 exit_code))367 ProcessFailedError(data, exit_code))
356368
357 def _cancel(self):369 def _cancel(self):
358 """370 """
359371
=== modified file 'landscape/manager/tests/test_scriptexecution.py'
--- landscape/manager/tests/test_scriptexecution.py 2016-06-15 23:20:02 +0000
+++ landscape/manager/tests/test_scriptexecution.py 2016-10-06 11:03:00 +0000
@@ -437,10 +437,41 @@
437 self.plugin.process_factory = factory437 self.plugin.process_factory = factory
438 self.plugin.size_limit = 100438 self.plugin.size_limit = 100
439 result = self.plugin.run_script("/bin/sh", "")439 result = self.plugin.run_script("/bin/sh", "")
440 result.addCallback(self.assertEqual, "x" * 100)440
441441 # Ultimately we assert that the resulting output is limited to
442 protocol = factory.spawns[0][0]442 # 100 bytes and indicates its truncation.
443 protocol.childDataReceived(1, "x" * 200)443 result.addCallback(self.assertEqual,
444 ("x" * 79) + "\n**OUTPUT TRUNCATED**")
445
446 protocol = factory.spawns[0][0]
447
448 # Push 200 bytes of output, so we trigger truncation.
449 protocol.childDataReceived(1, "x" * 200)
450
451 for fd in (0, 1, 2):
452 protocol.childConnectionLost(fd)
453 protocol.processEnded(Failure(ProcessDone(0)))
454
455 return result
456
457 def test_command_output_ends_with_truncation(self):
458 """After truncation, no further output is recorded."""
459 factory = StubProcessFactory()
460 self.plugin.process_factory = factory
461 self.plugin.size_limit = 100
462 result = self.plugin.run_script("/bin/sh", "")
463
464 # Ultimately we assert that the resulting output is limited to
465 # 100 bytes and indicates its truncation.
466 result.addCallback(self.assertEqual,
467 ("x" * 79) + "\n**OUTPUT TRUNCATED**")
468 protocol = factory.spawns[0][0]
469
470 # Push 200 bytes of output, so we trigger truncation.
471 protocol.childDataReceived(1, "x" * 200)
472 # Push 200 bytes more
473 protocol.childDataReceived(1, "x" * 200)
474
444 for fd in (0, 1, 2):475 for fd in (0, 1, 2):
445 protocol.childConnectionLost(fd)476 protocol.childConnectionLost(fd)
446 protocol.processEnded(Failure(ProcessDone(0)))477 protocol.processEnded(Failure(ProcessDone(0)))

Subscribers

People subscribed via source and target branches

to all changes: