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
1=== modified file 'landscape/manager/scriptexecution.py'
2--- landscape/manager/scriptexecution.py 2016-08-17 20:31:48 +0000
3+++ landscape/manager/scriptexecution.py 2016-10-06 11:03:00 +0000
4@@ -6,7 +6,6 @@
5 import os
6 import pwd
7 import tempfile
8-import operator
9 import shutil
10
11 from twisted.internet.protocol import ProcessProtocol
12@@ -95,6 +94,8 @@
13 process with.
14 """
15
16+ truncation_indicator = "\n**OUTPUT TRUNCATED**"
17+
18 def __init__(self, process_factory=None):
19 if process_factory is None:
20 from twisted.internet import reactor as process_factory
21@@ -124,7 +125,7 @@
22 gid = None
23
24 pp = ProcessAccumulationProtocol(
25- self.registry.reactor, self.size_limit)
26+ self.registry.reactor, self.size_limit, self.truncation_indicator)
27 self.process_factory.spawnProcess(
28 pp, filename, uid=uid, gid=gid, path=path, env=env)
29 if time_limit is not None:
30@@ -309,11 +310,15 @@
31 @ivar size_limit: The number of bytes at which to truncate output.
32 """
33
34- def __init__(self, reactor, size_limit):
35+ def __init__(self, reactor, size_limit, truncation_indicator=""):
36 self.data = []
37+ self._size = 0
38 self.result_deferred = Deferred()
39 self._cancelled = False
40 self.size_limit = size_limit
41+ self._truncation_indicator = truncation_indicator
42+ self._truncation_offset = len(self._truncation_indicator)
43+ self._truncated_size_limit = self.size_limit - self._truncation_offset
44 self.reactor = reactor
45 self._scheduled_cancel = None
46
47@@ -327,8 +332,15 @@
48 Add it to our buffer, as long as it doesn't go over L{size_limit}
49 bytes.
50 """
51- current_size = reduce(operator.add, map(len, self.data), 0)
52- self.data.append(data[:self.size_limit - current_size])
53+ if self._size < self.size_limit:
54+ data_length = len(data)
55+ if (self._size + data_length) >= self._truncated_size_limit:
56+ extent = (self._truncated_size_limit - self._size)
57+ self.data.append(data[:extent] + self._truncation_indicator)
58+ self._size = self.size_limit
59+ else:
60+ self.data.append(data)
61+ self._size += data_length
62
63 def processEnded(self, reason):
64 """Fire back the deferred.
65@@ -351,8 +363,8 @@
66 if reason.check(ProcessDone):
67 self.result_deferred.callback(data)
68 else:
69- self.result_deferred.errback(ProcessFailedError(data,
70- exit_code))
71+ self.result_deferred.errback(
72+ ProcessFailedError(data, exit_code))
73
74 def _cancel(self):
75 """
76
77=== modified file 'landscape/manager/tests/test_scriptexecution.py'
78--- landscape/manager/tests/test_scriptexecution.py 2016-06-15 23:20:02 +0000
79+++ landscape/manager/tests/test_scriptexecution.py 2016-10-06 11:03:00 +0000
80@@ -437,10 +437,41 @@
81 self.plugin.process_factory = factory
82 self.plugin.size_limit = 100
83 result = self.plugin.run_script("/bin/sh", "")
84- result.addCallback(self.assertEqual, "x" * 100)
85-
86- protocol = factory.spawns[0][0]
87- protocol.childDataReceived(1, "x" * 200)
88+
89+ # Ultimately we assert that the resulting output is limited to
90+ # 100 bytes and indicates its truncation.
91+ result.addCallback(self.assertEqual,
92+ ("x" * 79) + "\n**OUTPUT TRUNCATED**")
93+
94+ protocol = factory.spawns[0][0]
95+
96+ # Push 200 bytes of output, so we trigger truncation.
97+ protocol.childDataReceived(1, "x" * 200)
98+
99+ for fd in (0, 1, 2):
100+ protocol.childConnectionLost(fd)
101+ protocol.processEnded(Failure(ProcessDone(0)))
102+
103+ return result
104+
105+ def test_command_output_ends_with_truncation(self):
106+ """After truncation, no further output is recorded."""
107+ factory = StubProcessFactory()
108+ self.plugin.process_factory = factory
109+ self.plugin.size_limit = 100
110+ result = self.plugin.run_script("/bin/sh", "")
111+
112+ # Ultimately we assert that the resulting output is limited to
113+ # 100 bytes and indicates its truncation.
114+ result.addCallback(self.assertEqual,
115+ ("x" * 79) + "\n**OUTPUT TRUNCATED**")
116+ protocol = factory.spawns[0][0]
117+
118+ # Push 200 bytes of output, so we trigger truncation.
119+ protocol.childDataReceived(1, "x" * 200)
120+ # Push 200 bytes more
121+ protocol.childDataReceived(1, "x" * 200)
122+
123 for fd in (0, 1, 2):
124 protocol.childConnectionLost(fd)
125 protocol.processEnded(Failure(ProcessDone(0)))

Subscribers

People subscribed via source and target branches

to all changes: