Skip to content
This repository was archived by the owner on Jan 17, 2025. It is now read-only.

Commit 64be88a

Browse files
authored
switch to high performance action loop (#75)
1 parent 6f2b5b0 commit 64be88a

5 files changed

Lines changed: 93 additions & 77 deletions

File tree

python3.7/Dockerfile

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,5 @@
1-
FROM python:3.7-slim-stretch
2-
3-
ENV FLASK_PROXY_PORT 8080
4-
5-
RUN apt-get update && apt-get install -y \
6-
gcc \
7-
libc-dev \
8-
libxslt-dev \
9-
libxml2-dev \
10-
libffi-dev \
11-
libssl-dev \
12-
zip \
13-
unzip \
14-
vim \
15-
&& rm -rf /var/lib/apt/lists/*
16-
17-
RUN apt-cache search linux-headers-generic
1+
FROM openwhisk/actionloop-python-v3.7:d6a8920
182

193
COPY requirements.txt requirements.txt
204

215
RUN pip install --upgrade pip setuptools six && pip install --no-cache-dir -r requirements.txt
22-
23-
# create action working directory
24-
RUN mkdir -p /action
25-
26-
RUN mkdir -p /actionProxy
27-
ADD https://raw.githubusercontent.com/apache/incubator-openwhisk-runtime-docker/8b2e205c39d84ed5ede6b1b08cccf314a2b13105/core/actionProxy/actionproxy.py /actionProxy/actionproxy.py
28-
29-
RUN mkdir -p /pythonAction
30-
ADD https://raw.githubusercontent.com/apache/incubator-openwhisk-runtime-python/3%401.0.3/core/pythonAction/pythonrunner.py /pythonAction/pythonrunner.py
31-
32-
CMD ["/bin/bash", "-c", "cd /pythonAction && python -u pythonrunner.py"]

tests/src/test/scala/actionContainers/IBMPython37ActionContainerTests.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@ import org.scalatest.junit.JUnitRunner
2222
class IBMPython37ActionContainerTests extends IBMPythonActionContainerTests {
2323

2424
override lazy val imageName = "action-python-v3.7"
25+
override val testNoSource = TestConfig("", hasCodeStub = false)
26+
27+
/** actionloop based image does not log init errors - return the error in the body */
28+
override lazy val initErrorsAreLogged = false
2529
}

tests/src/test/scala/actionContainers/IBMPythonActionContainerTests.scala

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
3535
/** indicates if strings in python are unicode by default (i.e., python3 -> true, python2.7 -> false) */
3636
lazy val pythonStringAsUnicode = true
3737

38+
/** indicates if errors are logged or returned in the answer */
39+
lazy val initErrorsAreLogged = true
40+
3841
override def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit) = {
3942
withContainer(imageName, env)(code)
4043
}
@@ -202,11 +205,12 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
202205
initCode should be(502)
203206
}
204207

205-
checkStreams(out, err, {
206-
case (o, e) =>
207-
o shouldBe empty
208-
e should include("Zip file does not include")
209-
})
208+
if (initErrorsAreLogged)
209+
checkStreams(out, err, {
210+
case (o, e) =>
211+
o shouldBe empty
212+
e should include("Zip file does not include")
213+
})
210214
}
211215

212216
it should "report error if zipped Python action containing a virtual environment for wrong python version" in {
@@ -216,16 +220,25 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
216220

217221
val (out, err) = withActionContainer() { c =>
218222
val (initCode, initRes) = c.init(initPayload(code, main = "main"))
219-
initCode should be(200)
220-
val args = JsObject("msg" -> JsString("any"))
221-
val (runCode, runRes) = c.run(runPayload(args))
222-
runCode should be(502)
223+
if (initErrorsAreLogged) {
224+
initCode should be(200)
225+
val args = JsObject("msg" -> JsString("any"))
226+
val (runCode, runRes) = c.run(runPayload(args))
227+
runCode should be(502)
228+
} else {
229+
// it actually means it is actionloop
230+
// it checks the error at init time
231+
initCode should be(502)
232+
initRes.get.fields.get("error").get.toString() should include("No module")
233+
}
223234
}
224-
checkStreams(out, err, {
225-
case (o, e) =>
226-
o shouldBe empty
227-
e should include("ModuleNotFoundError")
228-
})
235+
236+
if (initErrorsAreLogged)
237+
checkStreams(out, err, {
238+
case (o, e) =>
239+
o shouldBe empty
240+
e should include("ModuleNotFoundError")
241+
})
229242
}
230243

231244
it should "report error if zipped Python action has wrong main module name" in {
@@ -237,11 +250,13 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
237250
val (initCode, initRes) = c.init(initPayload(code, main = "main"))
238251
initCode should be(502)
239252
}
240-
checkStreams(out, err, {
241-
case (o, e) =>
242-
o shouldBe empty
243-
e should include("Zip file does not include __main__.py")
244-
})
253+
254+
if (initErrorsAreLogged)
255+
checkStreams(out, err, {
256+
case (o, e) =>
257+
o shouldBe empty
258+
e should include("Zip file does not include __main__.py")
259+
})
245260
}
246261

247262
it should "report error if zipped Python action has invalid virtualenv directory" in {
@@ -252,11 +267,13 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
252267
val (initCode, initRes) = c.init(initPayload(code, main = "main"))
253268
initCode should be(502)
254269
}
255-
checkStreams(out, err, {
256-
case (o, e) =>
257-
o shouldBe empty
258-
e should include("Zip file does not include /virtualenv/bin/")
259-
})
270+
271+
if (initErrorsAreLogged)
272+
checkStreams(out, err, {
273+
case (o, e) =>
274+
o shouldBe empty
275+
e should include("Zip file does not include /virtualenv/bin/")
276+
})
260277
}
261278

262279
it should "return on action error when action fails" in {
@@ -273,7 +290,12 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
273290
initCode should be(200)
274291

275292
val (runCode, runRes) = c.run(runPayload(JsObject()))
276-
runCode should be(502)
293+
/* ActionLoop does not set 502 if there are application errors
294+
* Since it only receive a string from the application
295+
* it should parse the entire string in JSON just to find it is an "error"
296+
*/
297+
if (initErrorsAreLogged)
298+
runCode should be(502)
277299

278300
runRes shouldBe defined
279301
runRes.get.fields.get("error") shouldBe defined
@@ -298,11 +320,12 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
298320
initCode should be(502)
299321
}
300322

301-
checkStreams(out, err, {
302-
case (o, e) =>
303-
o shouldBe empty
304-
e should include("Traceback")
305-
})
323+
if (initErrorsAreLogged)
324+
checkStreams(out, err, {
325+
case (o, e) =>
326+
o shouldBe empty
327+
e should include("Traceback")
328+
})
306329
}
307330

308331
it should "support application errors" in {
@@ -337,17 +360,25 @@ class IBMPythonActionContainerTests extends BasicActionRunnerTests with WskActor
337360
| return { "error": "not reaching here" }
338361
""".stripMargin
339362

340-
val (initCode, res) = c.init(initPayload(code))
341-
initCode should be(200)
342-
343-
val (runCode, runRes) = c.run(runPayload(JsObject()))
344-
runCode should be(502)
363+
if (initErrorsAreLogged) {
364+
val (initCode, res) = c.init(initPayload(code))
365+
initCode should be(200)
366+
367+
val (runCode, runRes) = c.run(runPayload(JsObject()))
368+
runCode should be(502)
369+
} else {
370+
// action loop detects those errors at init time
371+
val (initCode, initRes) = c.init(initPayload(code))
372+
initCode should be(502)
373+
initRes.get.fields.get("error").get.toString() should include("Traceback")
374+
}
345375
}
346376

347-
checkStreams(out, err, {
348-
case (o, e) =>
349-
o shouldBe empty
350-
e should include("Traceback")
351-
})
377+
if (initErrorsAreLogged)
378+
checkStreams(out, err, {
379+
case (o, e) =>
380+
o shouldBe empty
381+
e should include("Traceback")
382+
})
352383
}
353384
}

tests/src/test/scala/system/basic/WskBasicIBMPython37Tests.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ import org.scalatest.junit.JUnitRunner
2323
class WskBasicIBMPython37Tests extends WskBasicIBMPythonTests {
2424
override lazy val kind = "python:3.7"
2525
override lazy val filename = "python37_stretch_virtualenv.zip"
26+
27+
/** actionloop based image does not log init errors - return the error in the body */
28+
override lazy val initErrorsAreLogged = false
2629
}

tests/src/test/scala/system/basic/WskBasicIBMPythonTests.scala

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ class WskBasicIBMPythonTests extends TestHelpers with WskTestHelpers with Matche
3535
implicit val wskprops = WskProps()
3636
val wsk: common.rest.WskRestOperations = new WskRestOperations
3737

38+
/** indicates if errors are logged or returned in the answer */
39+
lazy val initErrorsAreLogged = true
40+
3841
behavior of "Native Python Action"
3942

4043
it should "invoke an action and get the result" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
@@ -98,16 +101,18 @@ class WskBasicIBMPythonTests extends TestHelpers with WskTestHelpers with Matche
98101
}
99102
}
100103

101-
it should "invoke an invalid action and get error back" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
102-
val name = "bad code"
103-
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
104-
action.create(name, Some(TestUtils.getTestActionFilename("malformed.py")), kind = Some(kind))
105-
}
104+
if (initErrorsAreLogged) {
105+
it should "invoke an invalid action and get error back" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
106+
val name = "bad code"
107+
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
108+
action.create(name, Some(TestUtils.getTestActionFilename("malformed.py")), kind = Some(kind))
109+
}
106110

107-
withActivation(wsk.activation, wsk.action.invoke(name)) { activation =>
108-
activation.response.result.get.fields.get("error") shouldBe Some(
109-
JsString("The action failed to generate or locate a binary. See logs for details."))
110-
activation.logs.get.mkString("\n") should { not include ("pythonaction.py") and not include ("flask") }
111+
withActivation(wsk.activation, wsk.action.invoke(name)) { activation =>
112+
activation.response.result.get.fields.get("error") shouldBe Some(
113+
JsString("The action failed to generate or locate a binary. See logs for details."))
114+
activation.logs.get.mkString("\n") should { not include ("pythonaction.py") and not include ("flask") }
115+
}
111116
}
112117
}
113118

0 commit comments

Comments
 (0)