# 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