diff --git a/poc/Dockerfile b/poc/Dockerfile index b4e5d78..d0d2249 100644 --- a/poc/Dockerfile +++ b/poc/Dockerfile @@ -3,6 +3,5 @@ RUN apk update && apk add bash curl coreutils file RUN pip install pipenv COPY Pipfile Pipfile.lock /tmp/ RUN cd /tmp && pipenv install --system --deploy -COPY kapow /usr/bin COPY bin/* /usr/bin/ ENTRYPOINT ["/usr/bin/kapow"] diff --git a/poc/kapow b/poc/bin/kapow similarity index 83% rename from poc/kapow rename to poc/bin/kapow index 6832c9e..ba8237d 100755 --- a/poc/kapow +++ b/poc/bin/kapow @@ -26,6 +26,9 @@ import shlex import sys from aiohttp import web, StreamReader +import click +import requests + log = logging.getLogger('kapow') @@ -281,22 +284,21 @@ async def delete_route(request): ######################################################################## -async def run_init_script(app): +async def run_init_script(app, scripts): """ Run the init script if given, then wait for the shell to finish. """ - if len(sys.argv) == 1: + if not scripts: # No script given cmd = "/bin/bash" - elif len(sys.argv) == 2: - cmd = f"/bin/bash --init-file {sys.argv[1]}" else: - print(f"Usage: {sys.argv[0]} ") - os._exit(1) + filenames = " ".join(shlex.quote(f) for f in scripts) + cmd = f"/bin/bash --init-file <(cat {filenames})" shell_task = await asyncio.create_subprocess_shell( cmd, + executable="/bin/bash", env={**os.environ, "KAPOW_URL": "http://localhost:8080" }) @@ -308,11 +310,10 @@ async def run_init_script(app): async def start_background_tasks(app): loop = asyncio.get_running_loop() - app["debug_tasks"] = loop.create_task(run_init_script(app)) + app["debug_tasks"] = loop.create_task(run_init_script(app, app["scripts"])) -def kapow(): - """Start aiohttp app.""" +def start_kapow_server(scripts): app = web.Application(client_max_size=1024**3) app.add_routes([ # Control API @@ -325,9 +326,72 @@ def kapow(): web.get('/handlers/{id}/{field:.*}', get_field), web.put('/handlers/{id}/{field:.*}', set_field), ]) + app["scripts"] = scripts app.on_startup.append(start_background_tasks) web.run_app(app) +######################################################################## +# Command Lineagement # +######################################################################## + + +@click.group() +@click.pass_context +def kapow(ctx): + """Start aiohttp app.""" + pass + + +@kapow.command() +@click.argument("scripts", nargs=-1) +def server(scripts): + start_kapow_server(scripts) + +@kapow.group() +def route(): + pass + + +@route.command() +@click.option("-c", "--command", nargs=1) +@click.option("-e", "--entrypoint", default="/bin/sh -c") +@click.option("-X", "--method", default="GET") +@click.option("--url", envvar='KAPOW_URL') +@click.argument("url_pattern", nargs=1) +@click.argument("command_file", required=False) +def add(url_pattern, entrypoint, command, method, url, command_file): + if command: + # Command is given inline + source = command + elif command_file is None: + # No command + source = "" + elif command_file == '-': + # Read commands from stdin + source = sys.stdin.read() + else: + # Read commands from a file + with open(command_file, 'r', encoding='utf-8') as handler: + source = handler.read() + + response = requests.post(f"{url}/routes", + json={"method": method, + "url_pattern": url_pattern, + "entrypoint": entrypoint, + "command": source}) + response.raise_for_status() + print(response.json()) + + +@route.command() +@click.option("--url", envvar='KAPOW_URL') +@click.argument("route-id") +def remove(route_id, url): + response = requests.delete(f"{url}/routes/{route_id}") + response.raise_for_status() + print(response.json()) + + if __name__ == '__main__': kapow() diff --git a/poc/bin/kroute b/poc/bin/kroute deleted file mode 100755 index f60e9e1..0000000 --- a/poc/bin/kroute +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python - -# -# Copyright 2019 Banco Bilbao Vizcaya Argentaria, S.A. -# -# 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. -# - -import sys - -import click -import requests - -@click.group() -def kroute(): - pass - -@kroute.command() -@click.option("-c", "--command", nargs=1) -@click.option("-e", "--entrypoint", default="/bin/sh -c") -@click.option("-X", "--method", default="GET") -@click.option("--url", envvar='KAPOW_URL') -@click.argument("url_pattern", nargs=1) -@click.argument("command_file", required=False) -def add(url_pattern, entrypoint, command, method, url, command_file): - if command: - # Command is given inline - source = command - elif command_file is None: - # No command - source = "" - elif command_file == '-': - # Read commands from stdin - source = sys.stdin.read() - else: - # Read commands from a file - with open(command_file, 'r', encoding='utf-8') as handler: - source = handler.read() - - response = requests.post(f"{url}/routes", - json={"method": method, - "url_pattern": url_pattern, - "entrypoint": entrypoint, - "command": source}) - response.raise_for_status() - print(response.json()) - - -@kroute.command() -@click.option("--url", envvar='KAPOW_URL') -@click.argument("route-id") -def remove(route_id, url): - response = requests.delete(f"{url}/routes/{route_id}") - response.raise_for_status() - print(response.json()) - - -if __name__ == '__main__': - kroute() diff --git a/poc/examples/eval.pow b/poc/examples/eval.pow index 15fddb4..209be5d 100755 --- a/poc/examples/eval.pow +++ b/poc/examples/eval.pow @@ -16,4 +16,4 @@ # limitations under the License. # -kroute add -X POST '/eval' -c '$($(request /body) | response /stream)' +kapow route add -X POST '/eval' -c '$($(request /body) | response /stream)' diff --git a/poc/examples/nmap/Dockerfile b/poc/examples/nmap/Dockerfile index c1f1509..3a8cae1 100644 --- a/poc/examples/nmap/Dockerfile +++ b/poc/examples/nmap/Dockerfile @@ -3,4 +3,4 @@ RUN apk update && apk add nmap COPY nmap.pow /tmp/ RUN cd /tmp && pipenv install --system --deploy ENTRYPOINT ["/usr/bin/kapow"] -CMD ["/tmp/nmap.pow"] +CMD ["server", "/tmp/nmap.pow"] diff --git a/poc/examples/nmap/nmap.pow b/poc/examples/nmap/nmap.pow index b493921..e68eb4c 100755 --- a/poc/examples/nmap/nmap.pow +++ b/poc/examples/nmap/nmap.pow @@ -16,4 +16,4 @@ # limitations under the License. # -kroute add -X GET '/list/{ip}' -c 'nmap -sL $(request /matches/ip) | response /body' +kapow route add -X GET '/list/{ip}' -c 'nmap -sL $(request /matches/ip) | response /body' diff --git a/poc/examples/operator.pow b/poc/examples/operator.pow index 926d27e..6be8194 100755 --- a/poc/examples/operator.pow +++ b/poc/examples/operator.pow @@ -16,26 +16,26 @@ # limitations under the License. # -kroute add /list/files -c 'ls -la $(request /params/path) | response /body' +kapow route add /list/files -c 'ls -la $(request /params/path) | response /body' -kroute add /list/processes -c 'ps -aux | response /body' +kapow route add /list/processes -c 'ps -aux | response /body' -kroute add /show/cpuinfo -c 'response /body < /proc/cpuinfo' +kapow route add /show/cpuinfo -c 'response /body < /proc/cpuinfo' -kroute add /show/memory -c 'free -m | response /body' +kapow route add /show/memory -c 'free -m | response /body' -kroute add /show/disk -c 'df -h | response /body' +kapow route add /show/disk -c 'df -h | response /body' -kroute add /show/connections -c 'ss -pluton | response /body' +kapow route add /show/connections -c 'ss -pluton | response /body' -kroute add /show/mounts -c 'mount | response /body' +kapow route add /show/mounts -c 'mount | response /body' -kroute add /tail/dmesg - <<-'EOF' +kapow route add /tail/dmesg - <<-'EOF' response /headers/Content-Type text/plain dmesg -w | response /stream EOF -kroute add /tail/journal - <<-'EOF' +kapow route add /tail/journal - <<-'EOF' response /headers/Content-Type text/plain journalctl -f | response /stream EOF diff --git a/poc/examples/pandoc/pandoc.pow b/poc/examples/pandoc/pandoc.pow index 67d308d..22af342 100755 --- a/poc/examples/pandoc/pandoc.pow +++ b/poc/examples/pandoc/pandoc.pow @@ -16,11 +16,11 @@ # limitations under the License. # -kroute add -X POST --entrypoint '/bin/zsh -c' '/convert/{from}/{to}' - <<-'EOF' +kapow route add -X POST --entrypoint '/bin/zsh -c' '/convert/{from}/{to}' - <<-'EOF' pandoc --from=$(request /matches/from) \ --to=$(request /matches/to) \ --output=>(response /body) \ =(request /body) EOF -kroute add -X GET '/formats/input' -c 'pandoc --list-input-formats | response /body' -kroute add -X GET '/formats/output' -c 'pandoc --list-output-formats | grep -v pdf | response /body' +kapow route add -X GET '/formats/input' -c 'pandoc --list-input-formats | response /body' +kapow route add -X GET '/formats/output' -c 'pandoc --list-output-formats | grep -v pdf | response /body' diff --git a/poc/examples/pdfeditor/pdfeditor.pow b/poc/examples/pdfeditor/pdfeditor.pow index e1749db..b84c8fa 100755 --- a/poc/examples/pdfeditor/pdfeditor.pow +++ b/poc/examples/pdfeditor/pdfeditor.pow @@ -16,5 +16,5 @@ # limitations under the License. # -kroute add -X POST --entrypoint ./topdf '/editor/pdf' -kroute add / -c 'response /headers/Content-Type text/html && response /body < pdfeditor.html' +kapow route add -X POST --entrypoint ./topdf '/editor/pdf' +kapow route add / -c 'response /headers/Content-Type text/html && response /body < pdfeditor.html' diff --git a/poc/examples/torrent.pow b/poc/examples/torrent.pow index 2813641..c58ff45 100755 --- a/poc/examples/torrent.pow +++ b/poc/examples/torrent.pow @@ -16,7 +16,7 @@ # limitations under the License. # -kroute add / - <<-'EOF' +kapow route add / - <<-'EOF' response /headers/Content-Type text/html response /body <<-HTML @@ -27,7 +27,7 @@ kroute add / - <<-'EOF' HTML EOF -kroute add /save/magnet -e '/bin/bash -c' - <<-'EOF' +kapow route add /save/magnet -e '/bin/bash -c' - <<-'EOF' link=$(request /params/link) [ -z $link ] && response /status 400 && exit 0 @@ -40,4 +40,4 @@ kroute add /save/magnet -e '/bin/bash -c' - <<-'EOF' response /headers/Location /torrent/list EOF -kroute add /torrent/list -c 'response /body "Not Implemented Yet"' +kapow route add /torrent/list -c 'response /body "Not Implemented Yet"' diff --git a/spec/README.md b/spec/README.md index 11b67f8..baf4294 100644 --- a/spec/README.md +++ b/spec/README.md @@ -537,7 +537,7 @@ You can run it by ... Any compliant implementation of Kapow! must provide these commands: -### `kapow` +### `kapow server` This is the master command, that shows the help if invoked without args, and runs the sub-commands when provided to it.