You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by cs...@apache.org on 2018/02/10 02:31:38 UTC
[incubator-openwhisk-runtime-swift] branch master updated: stop
clone and own for actionproxy (#18)
This is an automated email from the ASF dual-hosted git repository.
csantanapr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-runtime-swift.git
The following commit(s) were added to refs/heads/master by this push:
new 8d2e23c stop clone and own for actionproxy (#18)
8d2e23c is described below
commit 8d2e23cd92d1c082380b5465c2fb34ee3e42288d
Author: Carlos Santana <cs...@gmail.com>
AuthorDate: Fri Feb 9 21:31:36 2018 -0500
stop clone and own for actionproxy (#18)
---
core/actionProxy/actionproxy.py | 283 ---------------------------
core/actionProxy/invoke.py | 102 ----------
core/swift3.1.1Action/Dockerfile | 3 +-
core/swift3.1.1Action/build.gradle | 12 --
core/swift4Action/Dockerfile | 3 +-
core/swift4Action/build.gradle | 11 --
tests/src/test/scala/sdk/SwiftSDKTests.scala | 12 ++
7 files changed, 14 insertions(+), 412 deletions(-)
diff --git a/core/actionProxy/actionproxy.py b/core/actionProxy/actionproxy.py
deleted file mode 100644
index 7021615..0000000
--- a/core/actionProxy/actionproxy.py
+++ /dev/null
@@ -1,283 +0,0 @@
-"""Executable Python script for a proxy service to dockerSkeleton.
-
-Provides a proxy service (using Flask, a Python web microframework)
-that implements the required /init and /run routes to interact with
-the OpenWhisk invoker service.
-
-The implementation of these routes is encapsulated in a class named
-ActionRunner which provides a basic framework for receiving code
-from an invoker, preparing it for execution, and then running the
-code when required.
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-"""
-
-import sys
-import os
-import json
-import subprocess
-import codecs
-import flask
-from gevent.wsgi import WSGIServer
-import zipfile
-import io
-import base64
-
-
-class ActionRunner:
- """ActionRunner."""
- LOG_SENTINEL = 'XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX'
-
- # initializes the runner
- # @param source the path where the source code will be located (if any)
- # @param binary the path where the binary will be located (may be the
- # same as source code path)
- def __init__(self, source=None, binary=None, zipdest=None):
- defaultBinary = '/action/exec'
- self.source = source if source else defaultBinary
- self.binary = binary if binary else defaultBinary
- self.zipdest = zipdest if zipdest else os.path.dirname(self.source)
-
- def preinit(self):
- return
-
- # extracts from the JSON object message a 'code' property and
- # writes it to the <source> path. The source code may have an
- # an optional <epilogue>. The source code is subsequently built
- # to produce the <binary> that is executed during <run>.
- # @param message is a JSON object, should contain 'code'
- # @return True iff binary exists and is executable
- def init(self, message):
- def prep():
- self.preinit()
- if 'code' in message and message['code'] is not None:
- binary = message['binary'] if 'binary' in message else False
- if not binary:
- return self.initCodeFromString(message)
- else:
- return self.initCodeFromZip(message)
- else:
- return False
-
- if prep():
- try:
- # write source epilogue if any
- # the message is passed along as it may contain other
- # fields relevant to a specific container.
- if self.epilogue(message) is False:
- return False
- # build the source
- if self.build(message) is False:
- return False
- except Exception:
- return False
- # verify the binary exists and is executable
- return self.verify()
-
- # optionally appends source to the loaded code during <init>
- def epilogue(self, init_arguments):
- return
-
- # optionally builds the source code loaded during <init> into an executable
- def build(self, init_arguments):
- return
-
- # @return True iff binary exists and is executable, False otherwise
- def verify(self):
- return (os.path.isfile(self.binary) and
- os.access(self.binary, os.X_OK))
-
- # constructs an environment for the action to run in
- # @param message is a JSON object received from invoker (should
- # contain 'value' and 'api_key' and other metadata)
- # @return an environment dictionary for the action process
- def env(self, message):
- # make sure to include all the env vars passed in by the invoker
- env = os.environ
- for p in ['api_key', 'namespace', 'action_name', 'activation_id', 'deadline']:
- if p in message:
- env['__OW_%s' % p.upper()] = message[p]
- return env
-
- # runs the action, called iff self.verify() is True.
- # @param args is a JSON object representing the input to the action
- # @param env is the environment for the action to run in (defined edge
- # host, auth key)
- # return JSON object result of running the action or an error dictionary
- # if action failed
- def run(self, args, env):
- def error(msg):
- # fall through (exception and else case are handled the same way)
- sys.stdout.write('%s\n' % msg)
- return (502, {'error': 'The action did not return a dictionary.'})
-
- try:
- input = json.dumps(args)
- if len(input) > 131071: # MAX_ARG_STRLEN (131071) linux/binfmts.h
- # pass argument via stdin
- p = subprocess.Popen(
- [self.binary],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=env)
- else:
- # pass argument via stdin and command parameter
- p = subprocess.Popen(
- [self.binary, input],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=env)
- # run the process and wait until it completes.
- # stdout/stderr will always be set because we passed PIPEs to Popen
- (o, e) = p.communicate(input=input.encode())
-
- except Exception as e:
- return error(e)
-
- # stdout/stderr may be either text or bytes, depending on Python
- # version, so if bytes, decode to text. Note that in Python 2
- # a string will match both types; so also skip decoding in that case
- if isinstance(o, bytes) and not isinstance(o, str):
- o = o.decode('utf-8')
- if isinstance(e, bytes) and not isinstance(e, str):
- e = e.decode('utf-8')
-
- # get the last line of stdout, even if empty
- lastNewLine = o.rfind('\n', 0, len(o)-1)
- if lastNewLine != -1:
- # this is the result string to JSON parse
- lastLine = o[lastNewLine+1:].strip()
- # emit the rest as logs to stdout (including last new line)
- sys.stdout.write(o[:lastNewLine+1])
- else:
- # either o is empty or it is the result string
- lastLine = o.strip()
-
- if e:
- sys.stderr.write(e)
-
- try:
- json_output = json.loads(lastLine)
- if isinstance(json_output, dict):
- return (200, json_output)
- else:
- return error(lastLine)
- except Exception:
- return error(lastLine)
-
- # initialize code from inlined string
- def initCodeFromString(self, message):
- with codecs.open(self.source, 'w', 'utf-8') as fp:
- fp.write(message['code'])
- return True
-
- # initialize code from base64 encoded archive
- def initCodeFromZip(self, message):
- try:
- bytes = base64.b64decode(message['code'])
- bytes = io.BytesIO(bytes)
- archive = zipfile.ZipFile(bytes)
- archive.extractall(self.zipdest)
- archive.close()
- return True
- except Exception as e:
- print('err', str(e))
- return False
-
-proxy = flask.Flask(__name__)
-proxy.debug = False
-runner = None
-
-
-def setRunner(r):
- global runner
- runner = r
-
-
-@proxy.route('/init', methods=['POST'])
-def init():
- message = flask.request.get_json(force=True, silent=True)
- if message and not isinstance(message, dict):
- flask.abort(404)
- else:
- value = message.get('value', {}) if message else {}
-
- if not isinstance(value, dict):
- flask.abort(404)
-
- try:
- status = runner.init(value)
- except Exception as e:
- status = False
-
- if status is True:
- return ('OK', 200)
- else:
- response = flask.jsonify({'error': 'The action failed to generate or locate a binary. See logs for details.'})
- response.status_code = 502
- return complete(response)
-
-
-@proxy.route('/run', methods=['POST'])
-def run():
- def error():
- response = flask.jsonify({'error': 'The action did not receive a dictionary as an argument.'})
- response.status_code = 404
- return complete(response)
-
- message = flask.request.get_json(force=True, silent=True)
- if message and not isinstance(message, dict):
- return error()
- else:
- args = message.get('value', {}) if message else {}
- if not isinstance(args, dict):
- return error()
-
- if runner.verify():
- try:
- 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. {}'.format(e)})
- response.status_code = 500
- else:
- response = flask.jsonify({'error': 'The action failed to locate a binary. See logs for details.'})
- response.status_code = 502
- return complete(response)
-
-
-def complete(response):
- # Add sentinel to stdout/stderr
- sys.stdout.write('%s\n' % ActionRunner.LOG_SENTINEL)
- sys.stdout.flush()
- sys.stderr.write('%s\n' % ActionRunner.LOG_SENTINEL)
- sys.stderr.flush()
- return response
-
-
-def main():
- port = int(os.getenv('FLASK_PROXY_PORT', 8080))
- server = WSGIServer(('', port), proxy, log=None)
- server.serve_forever()
-
-if __name__ == '__main__':
- setRunner(ActionRunner())
- main()
diff --git a/core/actionProxy/invoke.py b/core/actionProxy/invoke.py
deleted file mode 100755
index 92d8a6d..0000000
--- a/core/actionProxy/invoke.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-"""Executable Python script for testing the action proxy.
-
- This script is useful for testing the action proxy (or its derivatives)
- by simulating invoker interactions. Use it in combination with
- delete-build-run.sh which builds and starts up the action proxy.
- Examples:
- ./delete-build-run.sh &
- ./invoke.py init <action source file> # should return OK
- ./invoke.py run '{"some":"json object as a string"}'
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-"""
-
-import os
-import re
-import sys
-import json
-import requests
-import codecs
-
-DOCKER_HOST = "localhost"
-if "DOCKER_HOST" in os.environ:
- try:
- DOCKER_HOST = re.compile("tcp://(.*):[\d]+").findall(
- os.environ["DOCKER_HOST"])[0]
- except Exception:
- print("cannot determine docker host from %s" %
- os.environ["DOCKER_HOST"])
- sys.exit(-1)
-DEST = "http://%s:8080" % DOCKER_HOST
-
-
-def content_from_args(args):
- if len(args) == 0:
- return None
-
- if len(args) == 1 and os.path.exists(args[0]):
- with open(args[0]) as fp:
- return json.load(fp)
-
- # else...
- in_str = " ".join(args)
- try:
- d = json.loads(in_str)
- if isinstance(d, dict):
- return d
- else:
- raise "Not a dict."
- except:
- return in_str
-
-
-def init(args):
- main = args[1] if len(args) == 2 else "main"
- args = args[0] if len(args) >= 1 else None
-
- if args and args.endswith(".zip"):
- with open(args, "rb") as fp:
- contents = fp.read().encode("base64")
- binary = True
- elif args:
- with(codecs.open(args, "r", "utf-8")) as fp:
- contents = fp.read()
- binary = False
- else:
- contents = None
- binary = False
-
- r = requests.post("%s/init" % DEST, json={"value": {"code": contents,
- "binary": binary,
- "main": main}})
- print(r.text)
-
-
-def run(args):
- value = content_from_args(args)
- # print("Sending value: %s..." % json.dumps(value)[0:40])
- r = requests.post("%s/run" % DEST, json={"value": value})
- print(r.text)
-
-
-if sys.argv[1] == "init":
- init(sys.argv[2:])
-elif sys.argv[1] == "run":
- run(sys.argv[2:])
-else:
- print("usage: 'init <filename>' or 'run JSON-as-string'")
diff --git a/core/swift3.1.1Action/Dockerfile b/core/swift3.1.1Action/Dockerfile
index 352767f..d378947 100755
--- a/core/swift3.1.1Action/Dockerfile
+++ b/core/swift3.1.1Action/Dockerfile
@@ -10,8 +10,7 @@ RUN apt-get -y update \
&& apt-get -y install --fix-missing python2.7 python-gevent python-flask zip
# Add the action proxy
-RUN mkdir -p /actionProxy
-ADD actionproxy.py /actionProxy
+ADD https://raw.githubusercontent.com/apache/incubator-openwhisk-runtime-docker/dockerskeleton%401.1.0/core/actionProxy/actionproxy.py /actionProxy/actionproxy.py
# Add files needed to build and run action
RUN mkdir -p /swift3Action
diff --git a/core/swift3.1.1Action/build.gradle b/core/swift3.1.1Action/build.gradle
index c977a30..ecc3760 100755
--- a/core/swift3.1.1Action/build.gradle
+++ b/core/swift3.1.1Action/build.gradle
@@ -1,14 +1,2 @@
ext.dockerImageName = 'action-swift-v3.1.1'
apply from: '../../gradle/docker.gradle'
-distDocker.dependsOn 'copyProxy'
-distDocker.finalizedBy('cleanup')
-
-task copyProxy(type: Copy) {
- from '../actionProxy/actionproxy.py'
- into './actionproxy.py'
-}
-
-task cleanup(type: Delete) {
- delete 'actionproxy.py'
- delete 'swift3runner.py'
-}
diff --git a/core/swift4Action/Dockerfile b/core/swift4Action/Dockerfile
index 71a28ac..f14159d 100755
--- a/core/swift4Action/Dockerfile
+++ b/core/swift4Action/Dockerfile
@@ -10,8 +10,7 @@ RUN apt-get -y update \
&& apt-get -y install --fix-missing python2.7 python-gevent python-flask zip
# Add the action proxy
-RUN mkdir -p /actionProxy
-ADD actionproxy.py /actionProxy
+ADD https://raw.githubusercontent.com/apache/incubator-openwhisk-runtime-docker/dockerskeleton%401.1.0/core/actionProxy/actionproxy.py /actionProxy/actionproxy.py
# Add files needed to build and run action
RUN mkdir -p /swift4Action/spm-build/Sources/Action
diff --git a/core/swift4Action/build.gradle b/core/swift4Action/build.gradle
index 9ad1f58..d4357c5 100755
--- a/core/swift4Action/build.gradle
+++ b/core/swift4Action/build.gradle
@@ -1,13 +1,2 @@
ext.dockerImageName = 'action-swift-v4'
apply from: '../../gradle/docker.gradle'
-distDocker.dependsOn 'copyProxy'
-distDocker.finalizedBy('cleanup')
-
-task copyProxy(type: Copy) {
- from '../actionProxy/actionproxy.py'
- into '.'
-}
-
-task cleanup(type: Delete) {
- delete 'actionproxy.py'
-}
diff --git a/tests/src/test/scala/sdk/SwiftSDKTests.scala b/tests/src/test/scala/sdk/SwiftSDKTests.scala
index ad77386..14fcbee 100644
--- a/tests/src/test/scala/sdk/SwiftSDKTests.scala
+++ b/tests/src/test/scala/sdk/SwiftSDKTests.scala
@@ -98,10 +98,22 @@ abstract class SwiftSDKTests extends TestHelpers with WskTestHelpers with Matche
it should "allow Swift actions to trigger events" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
// create a trigger
val triggerName = s"TestTrigger ${System.currentTimeMillis()}"
+ val ruleName = s"TestTriggerRule ${System.currentTimeMillis()}"
+ val ruleActionName = s"TestTriggerAction ${System.currentTimeMillis()}"
assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
trigger.create(triggerName)
}
+ // create a dummy action
+ assetHelper.withCleaner(wsk.action, ruleActionName) { (action, name) =>
+ val dummyFile = Some(new File(actionTypeDir, "hello.swift").toString())
+ action.create(name, dummyFile, kind = Some(actionKind))
+ }
+ // create a dummy rule
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = ruleActionName)
+ }
+
// create an action that fires the trigger
val file = Some(new File(actionTypeDir, "trigger.swift").toString())
val actionName = "ActionThatTriggers"
--
To stop receiving notification emails like this one, please contact
csantanapr@apache.org.