diff options
Diffstat (limited to 'yarns/lib.py')
-rw-r--r-- | yarns/lib.py | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/yarns/lib.py b/yarns/lib.py new file mode 100644 index 0000000..d7c8a13 --- /dev/null +++ b/yarns/lib.py @@ -0,0 +1,165 @@ +# Copyright 2019 Lars Wirzenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +import base64 +import errno +import json +import os +import random +import re +import signal +import socket +import subprocess +import sys +import time +import urllib + +import cliapp +import requests + +from yarnutils import * + + + +class EffiAPI: + + config_filename = 'effiapi.json' + + def __init__(self, variables): + self.v = variables + + def write_config(self): + with open(self.config_filename, 'w') as f: + json.dump(self.get_config(), f, indent=4) + + def get_config(self): + return { + 'faks': True, + 'muck-url': 'xxx', + 'log': 'effiapi.log', + 'pid': 'effiapi.pid', + 'host': '127.0.0.1', + 'port': 8080, + } + + def start(self): + config = self.get_config() + self.v['baseurl'] = 'http://{}:{}'.format( + config['host'], config['port']) + + effiapi = os.path.join(srcdir, 'effiapi') + self.daemonize([effiapi, self.config_filename]) + + self.wait_for_port(config['host'], config['port']) + + def terminate(self): + config = self.get_config() + with open(config['pid']) as f: + pid = int(f.read().strip()) + os.kill(pid, signal.SIGTERM) + + def daemonize(self, argv): + prefix = [ + '/usr/sbin/daemonize', '-o', 'stdout', '-e', 'stderr', '-c', '.' + ] + subprocess.check_call(prefix + argv) + + def wait_for_port(self, host, port): + MAX = 5 + t = time.time() + while time.time() < t + MAX: + try: + s = socket.socket() + s.connect((host, port)) + except socket.error: + time.sleep(0.1) + except OSError as e: + raise + else: + return + + def POST(self, path, headers, body): + headers['Content-Type'] = 'application/json' + body = json.dumps(body) + self.request(requests.post, path, headers, body) + + def GET(self, path, headers, body): + self.request(requests.get, path, headers, body) + + def request(self, func, path, headers, body): + url = '{}{}'.format(self.v['baseurl'], path) + self.v['request'] = { + 'url': url, + 'func': repr(func), + 'headers': headers, + 'body': body, + } + + r = func(url, headers=headers, data=body) + self.v['response'] = { + 'status_code': r.status_code, + 'body': r.text, + 'headers': dict(r.headers), + } + + def get_status_code(self): + r = self.v['response'] + return r['status_code'] + + def get_header(self, name): + r = self.v['response'] + return r['headers'].get(name, '') + + def get_json_body(self): + r = self.v['response'] + return json.loads(r['body']) + + +def get_json_match(): + match = get_next_match() + return json.loads(match) + + +def save_for_expansion(name, value): + V[name] = value + + +def get_expanded_match(): + match = get_next_match() + print 'match', match + return expand(match, V) + + +def expand(text, variables): + result = '' + while text: + print 'expand: text=%r' % text + m = re.search(r'\${(?P<name>[^}]+)}', text) + if not m: + result += text + break + name = m.group('name') + print('expanding ', name, repr(variables[name])) + result += text[:m.start()] + variables[name] + text = text[m.end():] + print 'expand: result=%r' % result + return result + + +srcdir = os.environ['SRCDIR'] +datadir = os.environ['DATADIR'] +V = Variables(datadir) +effiapi = EffiAPI(V) |