summaryrefslogtreecommitdiff
path: root/yarns/lib.py
diff options
context:
space:
mode:
Diffstat (limited to 'yarns/lib.py')
-rw-r--r--yarns/lib.py214
1 files changed, 99 insertions, 115 deletions
diff --git a/yarns/lib.py b/yarns/lib.py
index 5fbd5ab..6d8f2cf 100644
--- a/yarns/lib.py
+++ b/yarns/lib.py
@@ -19,11 +19,13 @@ 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
@@ -37,119 +39,80 @@ datadir = os.environ['DATADIR']
V = Variables(datadir)
-def start_controller():
- port = V['port'] = random_free_port()
-
- V['url'] = 'http://127.0.0.1:{}'.format(V['port'])
-
- filename = 'ick_controller.yaml'
- env = dict(os.environ)
- env['ICK_CONTROLLER_CONFIG'] = filename
- write_yaml(filename, {
- 'token-issuer': V['issuer'],
- 'token-audience': V['audience'],
- 'token-public-key': cat('token.key.pub'),
- 'log': [
- {
- 'filename': 'ick_controller.log',
- },
- ],
- 'statedir': V['statedir'],
- 'apt-server': 'localhost',
- 'artifact-store': V['artifact_store'],
- 'auth-url': V['auth_url'],
- 'notify-url': V['notify_url'],
- })
-
- V['pid'] = gunicorn('ick_controller', 'app', port, env)
-
-
-def stop_controller():
- if V['pid'] is not None:
- os.kill(int(V['pid']), signal.SIGTERM)
-
-
-def start_artifact_store():
- port = V['bsport'] = random_free_port()
-
- V['bsurl'] = 'http://127.0.0.1:{}'.format(V['bsport'])
-
- filename = 'artifact_store.yaml'
- env = dict(os.environ)
- env['ARTIFACT_STORE_CONFIG'] = filename
- write_yaml(filename, {
- 'token-issuer': V['issuer'],
- 'token-audience': V['audience'],
- 'token-public-key': cat('token.key.pub'),
- 'log': [
- {
- 'filename': 'artifact_store.log',
- },
- ],
- 'blobdir': V['blobdir'],
- })
-
- V['bspid'] = gunicorn('artifact_store', 'app', port, env)
-
-
-def stop_artifact_store():
- if V['pid'] is not None:
- os.kill(int(V['bspid']), signal.SIGTERM)
-
-
-def write_yaml(filename, obj):
- yaml.safe_dump(obj, open(filename, 'w'))
-
-
-def gunicorn(module_name, var_name, port, env):
- log_filename = '{}.gunicorn.log'.format(module_name)
- pid_filename = '{}.pid'.format(module_name)
-
- argv = [
- 'gunicorn3',
- '--daemon',
- '--bind', '127.0.0.1:{}'.format(port),
- '--log-file', log_filename,
- '--log-level', 'debug',
- '-p', pid_filename,
- '{}:{}'.format(module_name, var_name),
- ]
- cliapp.runcmd(argv, env=env)
- wait_for_port(port)
- return int(cat(pid_filename))
-
-
-def random_free_port():
- MAX = 1000
- for i in range(MAX):
- port = random.randint(1025, 2**15-1)
- s = socket.socket()
- try:
- s.bind(('0.0.0.0', port))
- except OSError as e:
- if e.errno == errno.EADDRINUSE:
- continue
- print('cannot find a random free port')
- raise
- s.close()
- break
- print('picked port', port)
- return port
-
-
-def wait_for_port(port):
- MAX = 5
- t = time.time()
- while time.time() < t + MAX:
- try:
- s = socket.socket()
- s.connect(('127.0.0.1', port))
- except socket.error:
- time.sleep(0.1)
- except OSError as e:
- raise
- else:
- return
+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 = ''
@@ -186,6 +149,12 @@ def get_token(user):
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
@@ -201,6 +170,11 @@ def get(url, token):
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),
@@ -309,5 +283,15 @@ def list_diff(a, b):
return None
-def encode_basename(basename):
- return urllib.quote(basename, safe='')
+def expand_vars(text, variables):
+ result = ''
+ while text:
+ m = re.search(r'\${(?P<name>[^}]+)}', 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