diff --git a/core/actionProxy/actionproxy.py b/core/actionProxy/actionproxy.py index 5975e35cd014faf9931439c9ad06507235772fbe..1c1627983e947b2d219dca510852f92888e1d889 100644 --- a/core/actionProxy/actionproxy.py +++ b/core/actionProxy/actionproxy.py @@ -235,12 +235,11 @@ def run(): if runner.verify(): try: - (code, result) = runner.run(args, - runner.env(message if message else {})) + code, result = runner.run(args, runner.env(message or {})) response = flask.jsonify(result) response.status_code = code except Exception as e: - response = flask.jsonify({'error': 'Internal error.'}) + response = flask.jsonify({'error': 'Internal error. {}'.format(e)}) response.status_code = 500 else: response = flask.jsonify({'error': 'The action failed to locate ' diff --git a/core/python2Action/Dockerfile b/core/python2Action/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..0bb0d2049e0a78ead213b1e7ed3ac3a6027d754a --- /dev/null +++ b/core/python2Action/Dockerfile @@ -0,0 +1,38 @@ +# Dockerfile for Python 2 actions, similar to the Python 3-based core/pythonAction +FROM python:2.7.12-alpine + +# Upgrade and install basic Python dependencies +RUN apk add --no-cache \ + bash \ + bzip2-dev \ + gcc \ + libc-dev \ + libxslt-dev \ + libxml2-dev \ + libffi-dev \ + openssl-dev \ + python-dev + +# Install common modules for python +RUN pip install --no-cache-dir --upgrade pip setuptools \ + && pip install --no-cache-dir \ + gevent==1.1.2 \ + flask==0.11.1 \ + beautifulsoup4==4.5.1 \ + httplib2==0.9.2 \ + kafka_python==1.3.1 \ + lxml==3.6.4 \ + python-dateutil==2.5.3 \ + requests==2.11.1 \ + scrapy==1.1.2 \ + simplejson==3.8.2 \ + virtualenv==15.1.0 \ + twisted==16.4.0 + +ENV FLASK_PROXY_PORT 8080 + +RUN mkdir -p /pythonAction +ADD actionproxy.py /pythonAction/ +ADD pythonrunner.py /pythonAction/ + +CMD ["/bin/bash", "-c", "cd pythonAction && python -u pythonrunner.py"] diff --git a/core/python2Action/build.gradle b/core/python2Action/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..a7a3f61e65a37fd6b811d2daf98177e039338620 --- /dev/null +++ b/core/python2Action/build.gradle @@ -0,0 +1,19 @@ +ext.dockerImageName = 'python2action' +apply from: '../../gradle/docker.gradle' +distDocker.dependsOn 'copyFiles' +distDocker.finalizedBy 'rmFiles' + +def runners = files( + new File(project(':core:actionProxy').projectDir, 'actionproxy.py'), + new File(project(':core:pythonAction').projectDir, 'pythonrunner.py') +) + +task copyFiles(type: Copy) { + from runners + into '.' +} + +task rmFiles(type: Delete) { + delete runners.collect { it.getName() } +} + diff --git a/core/pythonAction/Dockerfile b/core/pythonAction/Dockerfile index e096e8e98b1c4b56840f0e41d1e67eaa727c928c..e2a1cf5b2f628defbf850bcd822e3b88d511b651 100644 --- a/core/pythonAction/Dockerfile +++ b/core/pythonAction/Dockerfile @@ -8,20 +8,20 @@ RUN apk add --no-cache \ libxslt-dev \ libxml2-dev \ libffi-dev \ - openssl-dev \ - python-dev + openssl-dev # Install common modules for python RUN pip install \ - beautifulsoup4==4.5.1 \ - httplib2==0.9.2 \ - kafka_python==1.3.1 \ - lxml==3.6.4 \ - python-dateutil==2.5.3 \ - requests==2.11.1 \ - scrapy==1.1.2 \ - simplejson==3.8.2 \ - twisted==16.4.0 + beautifulsoup4==4.5.3 \ + httplib2==0.10.3 \ + kafka_python==1.3.2 \ + lxml==3.7.3 \ + python-dateutil==2.6.0 \ + requests==2.13.0 \ + scrapy==1.3.3 \ + simplejson==3.10.0 \ + virtualenv==15.1.0 \ + twisted==17.1.0 ENV FLASK_PROXY_PORT 8080 diff --git a/core/pythonAction/build.gradle b/core/pythonAction/build.gradle index 5d192552ac7bb05e1acddbfc99b562ac249390d9..0149b7093cb9266b69a0f5f41a40ad4d0064bb6a 100644 --- a/core/pythonAction/build.gradle +++ b/core/pythonAction/build.gradle @@ -1,3 +1,3 @@ -ext.dockerImageName = 'pythonaction' +ext.dockerImageName = 'python3action' apply from: '../../gradle/docker.gradle' distDocker.dependsOn ':core:actionProxy:distDocker' diff --git a/settings.gradle b/settings.gradle index ed84a46672f24f493ba05bc62ddf6f50dfc9dee5..1955febbed7b9dfa879f982b9b9887ef605f380b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,6 +6,7 @@ include 'core:nodejsActionBase' include 'core:nodejs6Action' include 'core:actionProxy' include 'core:pythonAction' +include 'core:python2Action' include 'core:swift3Action' include 'core:javaAction' diff --git a/tests/build.gradle b/tests/build.gradle index f9e95d98ae02275b33679012abbbf654cd7acc4a..32275c2e1867f38ff58efd452ffcaa8f9b938042 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -27,6 +27,7 @@ test.dependsOn([ ':core:nodejs6Action:distDocker', ':core:actionProxy:distDocker', ':core:pythonAction:distDocker', + ':core:python2Action:distDocker', ':core:javaAction:distDocker', ':core:swift3Action:distDocker', ':sdk:docker:distDocker', diff --git a/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala b/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala index 7c1e5a2a69d857fdde6071abce0a53dbb65a12be..cee440bafefa82afa325651dbad712b8b53702f2 100644 --- a/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala +++ b/tests/src/test/scala/actionContainers/ActionProxyContainerTests.scala @@ -56,9 +56,10 @@ class ActionProxyContainerTests extends BasicActionRunnerTests with WskActorSyst val python = """ |#!/usr/bin/env python + |from __future__ import print_function |import sys - |print 'hello stdout' - |print >> sys.stderr, 'hello stderr' + |print('hello stdout') + |print('hello stderr', file=sys.stderr) |print(sys.argv[1]) """.stripMargin.trim @@ -87,10 +88,10 @@ class ActionProxyContainerTests extends BasicActionRunnerTests with WskActorSyst |#!/usr/bin/env python |import os | - |print '{ "api_host": "%s", "api_key": "%s", "namespace": "%s", "action_name" : "%s", "activation_id": "%s", "deadline": "%s" }' % ( + |print('{ "api_host": "%s", "api_key": "%s", "namespace": "%s", "action_name" : "%s", "activation_id": "%s", "deadline": "%s" }' % ( | os.environ['__OW_API_HOST'], os.environ['__OW_API_KEY'], | os.environ['__OW_NAMESPACE'], os.environ['__OW_ACTION_NAME'], - | os.environ['__OW_ACTIVATION_ID'], os.environ['__OW_DEADLINE']) + | os.environ['__OW_ACTIVATION_ID'], os.environ['__OW_DEADLINE'])) """.stripMargin.trim val perl = """ diff --git a/tests/src/test/scala/actionContainers/Python2ActionContainerTests.scala b/tests/src/test/scala/actionContainers/Python2ActionContainerTests.scala new file mode 100644 index 0000000000000000000000000000000000000000..74f1b3c17dd67dfb04ccd58904c573f8ad1d212f --- /dev/null +++ b/tests/src/test/scala/actionContainers/Python2ActionContainerTests.scala @@ -0,0 +1,30 @@ +/* + * Copyright 2015-2016 IBM Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package actionContainers + +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner +import common.WskActorSystem + +@RunWith(classOf[JUnitRunner]) +class Python2ActionContainerTests extends PythonActionContainerTests with WskActorSystem { + + override lazy val imageName = "python2action" + + /** indicates if strings in python are unicode by default (i.e., python3 -> true, python2.7 -> false) */ + override lazy val pythonStringAsUnicode = false +} diff --git a/tests/src/test/scala/actionContainers/PythonActionContainerTests.scala b/tests/src/test/scala/actionContainers/PythonActionContainerTests.scala index 06d9e1efd2057d406af9a93a2e5110d0bca3a621..f25ec9bbf86692c0dd4939dd0b38b595c1bd6c71 100644 --- a/tests/src/test/scala/actionContainers/PythonActionContainerTests.scala +++ b/tests/src/test/scala/actionContainers/PythonActionContainerTests.scala @@ -29,11 +29,16 @@ import common.WskActorSystem @RunWith(classOf[JUnitRunner]) class PythonActionContainerTests extends BasicActionRunnerTests with WskActorSystem { + lazy val imageName = "python3action" + + /** indicates if strings in python are unicode by default (i.e., python3 -> true, python2.7 -> false) */ + lazy val pythonStringAsUnicode = true + override def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit) = { - withContainer("pythonaction", env)(code) + withContainer(imageName, env)(code) } - behavior of "pythonaction" + behavior of imageName testNotReturningJson( """ @@ -137,13 +142,23 @@ class PythonActionContainerTests extends BasicActionRunnerTests with WskActorSys it should "handle unicode in source, input params, logs, and result" in { val (out, err) = withActionContainer() { c => - val code = """ - |def main(dict): - | sep = dict['delimiter'] - | str = sep + " ☃ ".decode('utf-8') + sep - | print(str.encode('utf-8')) - | return {"winter" : str } - """.stripMargin + val code = if (pythonStringAsUnicode) { + """ + |def main(args): + | sep = args['delimiter'] + | s = sep + " ☃ " + sep + | print(s) + | return {"winter" : s } + """.stripMargin + } else { + """ + |def main(args): + | sep = args['delimiter'] + | s = sep + " ☃ ".decode('utf-8') + sep + | print(s.encode('utf-8')) + | return {"winter" : s } + """.stripMargin + } val (initCode, _) = c.init(initPayload(code)) initCode should be(200)