blob: 1fe1c0eaf1e8e65a463a08b5e991304093504fea [file] [log] [blame]
#!/usr/bin/env python
"""Executable Python script for testing the action proxy.
/*
* 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.
*/
This script is useful for testing the action proxy (or its derivatives)
by simulating invoker interactions. Use it in combination with
docker run <image> which starts up the action proxy.
Example:
docker run -i -t -p 8080:8080 dockerskeleton # locally built images may be referenced without a tag
./invoke.py init <action source file>
./invoke.py run '{"some":"json object as a string"}'
For additional help, try ./invoke.py -h
"""
import os
import re
import sys
import json
import base64
import requests
import codecs
import argparse
try:
import argcomplete
except ImportError:
argcomplete = False
def main():
try:
args = parseArgs()
exitCode = {
'init' : init,
'run' : run
}[args.cmd](args)
except Exception as e:
print(e)
exitCode = 1
sys.exit(exitCode)
def dockerHost():
dockerHost = 'localhost'
if 'DOCKER_HOST' in os.environ:
try:
dockerHost = 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)
return dockerHost
def containerRoute(args, path):
return 'http://%s:%s/%s' % (args.host, args.port, path)
class objectify(object):
def __init__(self, d):
self.__dict__ = d
def parseArgs():
parser = argparse.ArgumentParser(description='initialize and run an OpenWhisk action container')
parser.add_argument('-v', '--verbose', help='verbose output', action='store_true')
parser.add_argument('--host', help='action container host', default=dockerHost())
parser.add_argument('-p', '--port', help='action container port number', default=8080, type=int)
subparsers = parser.add_subparsers(title='available commands', dest='cmd')
initmenu = subparsers.add_parser('init', help='initialize container with src or zip/tgz file')
initmenu.add_argument('-b', '--binary', help='treat artifact as binary', action='store_true')
initmenu.add_argument('-r', '--run', nargs='?', default=None, help='run after init')
initmenu.add_argument('main', nargs='?', default='main', help='name of the "main" entry method for the action')
initmenu.add_argument('artifact', help='a source file or zip/tgz archive')
initmenu.add_argument('env', nargs='?', help='the environment variables to export to the action, either a reference to a file or an inline JSON object', default=None)
runmenu = subparsers.add_parser('run', help='send arguments to container to run action')
runmenu.add_argument('payload', nargs='?', help='the arguments to send to the action, either a reference to a file or an inline JSON object', default=None)
if argcomplete:
argcomplete.autocomplete(parser)
return parser.parse_args()
def init(args):
main = args.main
artifact = args.artifact
if artifact and (args.binary or artifact.endswith('.zip') or artifact.endswith('tgz') or artifact.endswith('jar')):
with open(artifact, 'rb') as fp:
contents = fp.read()
contents = str(base64.b64encode(contents), 'utf-8')
binary = True
elif artifact is not '':
with(codecs.open(artifact, 'r', 'utf-8')) as fp:
contents = fp.read()
binary = False
else:
contents = None
binary = False
r = requests.post(
containerRoute(args, 'init'),
json = {
"value": {
"code": contents,
"binary": binary,
"main": main,
"env": processPayload(args.env)
}
})
print(r.text)
if r.status_code == 200 and args.run is not None:
runArgs = objectify({})
runArgs.__dict__ = args.__dict__.copy()
runArgs.payload = args.run
run(runArgs)
def run(args):
value = processPayload(args.payload)
if args.verbose:
print('Sending value: %s...' % json.dumps(value)[0:40])
r = requests.post(containerRoute(args, 'run'), json = {"value": value})
print(r.text)
def processPayload(payload):
if payload and os.path.exists(payload):
with open(payload) as fp:
return json.load(fp)
try:
d = json.loads(payload if payload else '{}')
if isinstance(d, dict):
return d
else:
raise
except:
print('payload must be a JSON object.')
sys.exit(-1)
if __name__ == '__main__':
main()