# Copyright 2017-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 . import base64 import errno import json import os import random import re import signal import socket import sys import time import urllib import uuid import cliapp import requests import yaml from yarnutils import * srcdir = os.environ['SRCDIR'] datadir = os.environ['DATADIR'] V = Variables(datadir) def remember_client_id(alias, client_id, client_secret): clients = V['clients'] if clients is None: clients = {} clients[alias] = { 'client_id': client_id, 'client_secret': client_secret, } V['clients'] = clients def get_client_id(alias): clients = V['clients'] or {} return clients[alias]['client_id'] def get_client_ids(): clients = V['clients'] or {} return [x['client_id'] for x in clients.values()] def get_client_secret(alias): clients = V['clients'] or {} return clients[alias]['client_secret'] def create_api_client(alias, scopes): client_id = str(uuid.uuid4()) client_secret = str(uuid.uuid4()) print('invented client id', client_id) api = os.environ['CONTROLLER'] print('controller URL', api) secrets = os.environ['SECRETS'] print('secrets', secrets) base_argv = ['qvisqvetool', '--secrets', secrets, '-a', api] print('base_argv', base_argv) cliapp.runcmd(base_argv + ['create', 'client', client_id, client_secret]) cliapp.runcmd(base_argv + ['allow-scope', 'client', client_id] + scopes) remember_client_id(alias, client_id, client_secret) def delete_api_client(client_id): api = os.environ['CONTROLLER'] secrets = os.environ['SECRETS'] base_argv = ['qvisqvetool', '--secrets', secrets, '-a', api] cliapp.runcmd(base_argv + ['delete', 'client', client_id]) def get_api_token(alias, scopes): print('getting token for', alias) client_id = get_client_id(alias) client_secret = get_client_secret(alias) api = os.environ['CONTROLLER'] auth = (client_id, client_secret) data = { 'grant_type': 'client_credentials', 'scope': ' '.join(scopes), } url = '{}/token'.format(api) print('url', url) print('auth', auth) print('data', data) r = requests.post(url, auth=auth, data=data) if not r.ok: sys.exit('Error getting token: %s %s' % (r.status_code, r.text)) token = r.json()['access_token'] print('token', token) return token def unescape(s): t = '' while s: if s.startswith('\\n'): t += '\n' s = s[2:] else: t += s[0] s = s[1:] return t def write(filename, data): with open(filename, 'w') as f: f.write(data) def cat(filename): MAX_CAT_WAIT = 5 # in seconds t = time.time() while time.time() < t + MAX_CAT_WAIT: if os.path.exists(filename): return open(filename, 'r').read() def store_token(user, token): filename = '{}.jwt'.format(user) write(filename, token) def get_token(user): filename = '{}.jwt'.format(user) return cat(filename) def http(V, func, url, **kwargs): V['request'] = { 'func': repr(func), 'url': url, 'kwargs': kwargs, } print('http', func, url, kwargs) status, content_type, headers, body = func(url, **kwargs) V['status_code'] = status V['content_type'] = content_type V['headers'] = headers V['body'] = body def get(url, token): headers = { 'Authorization': 'Bearer {}'.format(token), } r = requests.get(url, headers=headers, verify=False) return r.status_code, r.headers['Content-Type'], dict(r.headers), r.text def get_version(url): status, ctype, headers, text = get(url + '/version', 'no token') assert ctype == 'application/json' return json.loads(text) def get_blob(url, token): headers = { 'Authorization': 'Bearer {}'.format(token), } r = requests.get(url, headers=headers, verify=False) return r.status_code, r.headers['Content-Type'], dict(r.headers), r.content def post(url, body, token): headers = { 'Authorization': 'Bearer {}'.format(token), 'Content-Type': 'application/json', } r = requests.post(url, headers=headers, data=body, verify=False) return r.status_code, r.headers['Content-Type'], dict(r.headers), r.text def put(url, body, token): headers = { 'Authorization': 'Bearer {}'.format(token), 'Content-Type': 'application/json', } r = requests.put(url, headers=headers, data=body, verify=False) return r.status_code, r.headers['Content-Type'], dict(r.headers), r.text def put_blob(url, body, token): headers = { 'Authorization': 'Bearer {}'.format(token), 'Content-Type': 'application/octet-stream', } r = requests.put(url, headers=headers, data=body, verify=False) return r.status_code, r.headers['Content-Type'], dict(r.headers), r.text def delete(url, token): headers = { 'Authorization': 'Bearer {}'.format(token), } r = requests.delete(url, headers=headers, verify=False) return r.status_code, r.headers['Content-Type'], dict(r.headers), r.text def dict_diff(a, b): if not isinstance(a, dict): return 'first value is not a dict' if not isinstance(b, dict): return 'second value is not a dict' delta = [] for key in a: if key not in b: delta.append('second does not have key {}'.format(key)) elif isinstance(a[key], dict): delta2 = dict_diff(a[key], b[key]) if delta2 is not None: delta.append('key {}: dict values differ:'.format(key)) delta.append(delta2) elif isinstance(a[key], list): delta2 = list_diff(a[key], b[key]) if delta2 is not None: delta.append('key {}: list values differ:'.format(key)) delta.append(delta2) elif a[key] != b[key]: delta.append('key {}: values differ'.format(key)) delta.append(' first value : {!r}'.format(a[key])) delta.append(' second value: {!r}'.format(b[key])) for key in b: if key not in a: delta.append('first does not have key {}'.format(key)) if delta: return '\n'.join(delta) return None def list_diff(a, b): if not isinstance(a, list): return 'first value is not a list' if not isinstance(b, list): return 'second value is not a list' delta = [] for i in range(len(a)): if i >= len(b): delta.append('second list is shorter than first') break elif isinstance(a[i], dict): delta2 = dict_diff(a[i], b[i]) if delta2 is not None: delta.append('item {}: items are different dicts'.format(i)) delta.append(delta2) elif a[i] != b[i]: delta.append('item %d: values differ'.format(i)) delta.append(' first value : {!r}'.format(a[i])) delta.append(' second value: {!r}'.format(b[i])) if len(a) < len(b): delta.append('first list is shorter than second') if delta: return '\n'.join(delta) return None def expand_vars(text, variables): result = '' while text: m = re.search(r'\${(?P[^}]+)}', text) if not m: result += text break name = m.group('name') print('expanding ', name) result += text[:m.start()] + variables[name] text = text[m.end():] return result