summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2019-07-21 11:45:29 +0300
committerLars Wirzenius <liw@liw.fi>2019-08-03 21:06:50 +0300
commit7a6bc2f7749c10c1d3344ea0196e81ae95b7f0d7 (patch)
treee52472ac9665bc21886ccf27b7a79e76079d6afe
parent4f2207571928dc685102e3b3ff55e79cd3d46b6d (diff)
downloadick2-7a6bc2f7749c10c1d3344ea0196e81ae95b7f0d7.tar.gz
Add: TokenGetter
-rw-r--r--ick2/__init__.py1
-rw-r--r--ick2/apibase.py4
-rw-r--r--ick2/controllerapi.py10
-rw-r--r--ick2/projectapi.py2
-rw-r--r--ick2/projectapi_tests.py11
-rw-r--r--ick2/tokengetter.py70
-rw-r--r--ick2/workapi.py3
-rw-r--r--ick2/workapi_tests.py16
-rw-r--r--ick2/workerapi.py3
-rw-r--r--ick_controller.py6
-rw-r--r--without-tests1
11 files changed, 119 insertions, 8 deletions
diff --git a/ick2/__init__.py b/ick2/__init__.py
index 235ee96..586ddb5 100644
--- a/ick2/__init__.py
+++ b/ick2/__init__.py
@@ -89,6 +89,7 @@ from .client import (
AuthClient,
Reporter,
)
+from .tokengetter import TokenGetter
from .actionenvs import (
Runner,
ActionEnvironment,
diff --git a/ick2/apibase.py b/ick2/apibase.py
index 46c17cf..0537b6b 100644
--- a/ick2/apibase.py
+++ b/ick2/apibase.py
@@ -26,6 +26,10 @@ class APIbase:
isinstance(state, ick2.MemoryStore) or
isinstance(state, ick2.MuckStore))
self._trans = ick2.TransactionalState(state)
+ self._token_getter = None
+
+ def set_token_getter(self, getter): # pragma: no cover
+ self._token_getter = getter
def get_routes(self, path):
resource_path = '{}/<name:re:[^/+][^/]*?(/[^/+][^/]*?)*>'.format(path)
diff --git a/ick2/controllerapi.py b/ick2/controllerapi.py
index b28e1f7..a673e0d 100644
--- a/ick2/controllerapi.py
+++ b/ick2/controllerapi.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017-2018 Lars Wirzenius
+# Copyright (C) 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
@@ -18,8 +18,9 @@ import ick2
class ControllerAPI:
- def __init__(self, state):
+ def __init__(self, state, token_getter):
self._state = state
+ self._token_getter = token_getter
self._apis = {}
def set_apt_server(self, domain): # pragma: no cover
@@ -30,6 +31,7 @@ class ControllerAPI:
def set_auth_url(self, url): # pragma: no cover
self._set_url('set_auth_url', url)
+ self._token_getter.set_auth_url(url)
def set_notify_url(self, url): # pragma: no cover
self._set_url('set_notify_url', url)
@@ -60,7 +62,9 @@ class ControllerAPI:
for path in apis:
if path not in self._apis:
- self._apis[path] = apis[path](self._state)
+ api = apis[path](self._state)
+ api.set_token_getter(self._token_getter)
+ self._apis[path] = api
routes = []
for path, api in self._apis.items():
diff --git a/ick2/projectapi.py b/ick2/projectapi.py
index a854716..00fce8a 100644
--- a/ick2/projectapi.py
+++ b/ick2/projectapi.py
@@ -50,7 +50,7 @@ class ProjectAPI(ick2.ResourceApiBase):
]
def trigger_project(self, project, **kwargs): # pragma: no cover
- token = 'FIXME'
+ token = self._token_getter.get_token()
with self._trans.modify(token, 'projects', project) as p:
self._start_build(token, p)
return {'status': ick2.BUILD_TRIGGERED}
diff --git a/ick2/projectapi_tests.py b/ick2/projectapi_tests.py
index f8b3c2a..bdb58d1 100644
--- a/ick2/projectapi_tests.py
+++ b/ick2/projectapi_tests.py
@@ -20,6 +20,12 @@ import unittest
import ick2
+class DummyTokenGetter:
+
+ def get_token(self):
+ return 'DUMMY.TOKEN'
+
+
class ProjectAPITests(unittest.TestCase):
def setUp(self):
@@ -29,7 +35,10 @@ class ProjectAPITests(unittest.TestCase):
return ick2.ProjectAPI(self.state)
def create_pipeline_api(self):
- return ick2.PipelineAPI(self.state)
+ getter = DummyTokenGetter()
+ api = ick2.PipelineAPI(self.state)
+ api.set_token_getter(getter)
+ return api
def test_has_not_projects_initially(self):
api = self.create_api()
diff --git a/ick2/tokengetter.py b/ick2/tokengetter.py
new file mode 100644
index 0000000..c152148
--- /dev/null
+++ b/ick2/tokengetter.py
@@ -0,0 +1,70 @@
+# Copyright (C) 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 time
+
+
+import jwt
+
+
+import ick2
+
+
+class TokenGetter:
+
+ scopes = [
+ 'super',
+ 'create',
+ 'update',
+ 'show',
+ 'delete',
+ 'uapi_workers_post',
+ 'uapi_workers_id_get',
+ 'uapi_workers_id_put',
+ 'uapi_workers_id_delete',
+ 'uapi_builds_post',
+ 'uapi_builds_id_get',
+ 'uapi_builds_id_put',
+ 'uapi_builds_id_delete',
+ 'uapi_logs_post',
+ 'uapi_logs_id_get',
+ 'uapi_logs_id_put',
+ 'uapi_logs_id_delete',
+ ]
+
+ def __init__(self, client_id, client_secret):
+ self._ac = ick2.AuthClient()
+ self._ac.set_client_creds(client_id, client_secret)
+ self._token = None
+ self._token_exp = None
+
+ def set_auth_url(self, auth_url):
+ self._ac.set_auth_url(auth_url)
+
+ def get_token(self):
+ if not self._got_valid_token():
+ self._get_new_token()
+ return self._token
+
+ def _got_valid_token(self):
+ fuzz = 10
+ return (self._token is not None and
+ self._token_exp is not None and
+ time.time() + fuzz < self._token_exp)
+
+ def _get_new_token(self):
+ self._token = self._ac.get_token(' '.join(self.scopes))
+ parsed = jwt.decode(self._token, verify=False)
+ self._token_exp = parsed['exp']
diff --git a/ick2/workapi.py b/ick2/workapi.py
index 3eeb0fa..7ff6f93 100644
--- a/ick2/workapi.py
+++ b/ick2/workapi.py
@@ -41,6 +41,7 @@ class WorkAPI(ick2.APIbase):
]
def get_work(self, token=None, **kwargs):
+ token = self._token_getter.get_token()
worker_id = self._get_client_id(**kwargs)
ick2.log.log(
'trace', msg_text='Worker wants work', worker_id=worker_id)
@@ -140,6 +141,8 @@ class WorkAPI(ick2.APIbase):
return None
def update_work(self, update, token=None, **kwargs):
+ token = self._token_getter.get_token()
+
try:
worker_id = update['worker']
build_id = update['build_id']
diff --git a/ick2/workapi_tests.py b/ick2/workapi_tests.py
index 72bcaf1..0ac3a86 100644
--- a/ick2/workapi_tests.py
+++ b/ick2/workapi_tests.py
@@ -36,7 +36,9 @@ class WorkAPITests(unittest.TestCase):
],
}
+ getter = DummyTokenGetter()
pipeapi = ick2.PipelineAPI(self.state)
+ pipeapi.set_token_getter(getter)
pipeapi.create(pipeline)
project = {
@@ -47,6 +49,7 @@ class WorkAPITests(unittest.TestCase):
'pipelines': ['build'],
}
api = ick2.ProjectAPI(self.state)
+ api.set_token_getter(getter)
api.create(project)
return api
@@ -57,12 +60,17 @@ class WorkAPITests(unittest.TestCase):
self.claims = {
'aud': 'asterix',
}
+ getter = DummyTokenGetter()
api = ick2.WorkerAPI(self.state)
+ api.set_token_getter(getter)
api.create(worker, claims=self.claims)
return api
def create_work_api(self):
- return ick2.WorkAPI(self.state)
+ getter = DummyTokenGetter()
+ api = ick2.WorkAPI(self.state)
+ api.set_token_getter(getter)
+ return api
def test_worker_gets_no_work_when_no_builds_have_been_triggered(self):
self.create_project_api()
@@ -287,3 +295,9 @@ class WorkAPITests(unittest.TestCase):
# Ask for work again.
self.assertEqual(work.get_work(claims=self.claims), {})
+
+
+class DummyTokenGetter:
+
+ def get_token(self):
+ return 'DUMMY.TOKEN'
diff --git a/ick2/workerapi.py b/ick2/workerapi.py
index d4b508c..9a2c5f2 100644
--- a/ick2/workerapi.py
+++ b/ick2/workerapi.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2017-2018 Lars Wirzenius
+# Copyright (C) 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
@@ -34,4 +34,5 @@ class WorkerAPI(ick2.ResourceApiBase): # pragma: no cover
def create(self, body, **kwargs):
client_id = self._get_client_id(**kwargs)
body['worker'] = client_id
+ kwargs['token'] = self._token_getter.get_token()
return super().create(body, **kwargs)
diff --git a/ick_controller.py b/ick_controller.py
index 62ccaef..7299e60 100644
--- a/ick_controller.py
+++ b/ick_controller.py
@@ -51,6 +51,8 @@ default_config = {
'auth-url': None,
'notify-url': None,
'apt-server': None,
+ 'client-id': None,
+ 'client-secret': None,
}
@@ -96,7 +98,9 @@ def main():
state = ick2.MuckStore(config['muck-url'])
- api = ick2.ControllerAPI(state)
+ getter = ick2.TokenGetter(config['client-id'], config['client-secret'])
+
+ api = ick2.ControllerAPI(state, getter)
api.set_apt_server(config['apt-server'])
api.set_artifact_store_url(config['artifact-store'])
api.set_auth_url(config['auth-url'])
diff --git a/without-tests b/without-tests
index ca8a65a..673068e 100644
--- a/without-tests
+++ b/without-tests
@@ -11,6 +11,7 @@ ick2/notificationapi.py
ick2/pipelineapi.py
ick2/resource.py
ick2/responses.py
+ick2/tokengetter.py
ick2/trans.py
ick2/sendmail.py
ick2/version.py