From 415f56289674297e5aec406960ec5c4b02adce89 Mon Sep 17 00:00:00 2001 From: cclauss <cclauss@bluewin.ch> Date: Fri, 24 Feb 2017 10:45:12 +0100 Subject: [PATCH] Add python:3 action support. Update tests to work in both Python 2 and Python 3. Rename pythonaction to python3action for container image name for clarity. Add tests for python:2 and python:3. Add image names for all the actions. Rename javaaction to java8action for consistency. --- core/actionProxy/actionproxy.py | 5 +-- core/python2Action/Dockerfile | 38 +++++++++++++++++++ core/python2Action/build.gradle | 19 ++++++++++ core/pythonAction/Dockerfile | 22 +++++------ core/pythonAction/build.gradle | 2 +- settings.gradle | 1 + tests/build.gradle | 1 + .../ActionProxyContainerTests.scala | 9 +++-- .../Python2ActionContainerTests.scala | 30 +++++++++++++++ .../PythonActionContainerTests.scala | 33 +++++++++++----- 10 files changed, 132 insertions(+), 28 deletions(-) create mode 100644 core/python2Action/Dockerfile create mode 100644 core/python2Action/build.gradle create mode 100644 tests/src/test/scala/actionContainers/Python2ActionContainerTests.scala diff --git a/core/actionProxy/actionproxy.py b/core/actionProxy/actionproxy.py index 5975e35c..1c162798 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 00000000..0bb0d204 --- /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 00000000..a7a3f61e --- /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 e096e8e9..e2a1cf5b 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 5d192552..0149b709 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 ed84a466..1955febb 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 f9e95d98..32275c2e 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 7c1e5a2a..cee440ba 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 00000000..74f1b3c1 --- /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 06d9e1ef..f25ec9bb 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) -- GitLab