← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~jtv/maas/store-worker-api-credentials into lp:maas

 

Jeroen T. Vermeulen has proposed merging lp:~jtv/maas/store-worker-api-credentials into lp:maas.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~jtv/maas/store-worker-api-credentials/+merge/118866

This builds on existing infrastructure for recording worker secrets.  We may need to adapt that infrastructure to support multi-processing, but that is a largely isolated change.

Julian just told me the format in which credentials are passed (which I had completely forgotten) but the tuple representation is a direct fit to what we already have coded up in the API client.


Jeroen
-- 
https://code.launchpad.net/~jtv/maas/store-worker-api-credentials/+merge/118866
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/maas/store-worker-api-credentials into lp:maas.
=== added file 'src/provisioningserver/auth.py'
--- src/provisioningserver/auth.py	1970-01-01 00:00:00 +0000
+++ src/provisioningserver/auth.py	2012-08-09 04:22:20 +0000
@@ -0,0 +1,46 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""API credentials for node-group workers."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = [
+    'get_recorded_api_credentials',
+    'record_api_credentials',
+    ]
+
+# API credentials as last sent by the server.  The worker uses these
+# credentials to access the MAAS API.
+# Shared between threads.
+recorded_api_credentials = None
+
+
+def record_api_credentials(api_credentials):
+    """Update the recorded API credentials.
+
+    :param api_credentials: Newly received API credentials, in the form of
+        a single string: consumer key, resource token, and resource seret
+        separated by colons.
+    """
+    global recorded_api_credentials
+    recorded_api_credentials = api_credentials
+
+
+def get_recorded_api_credentials():
+    """Return API credentials as last received from the server.
+
+    :return: If credentials have been received, a tuple of
+        (consumer_key, resource_token, resource_secret) as expected by
+        :class:`MAASOauth`.  Otherwise, None.
+    """
+    credentials_string = recorded_api_credentials
+    if credentials_string is None:
+        return None
+    else:
+        return tuple(credentials_string.split(':'))

=== modified file 'src/provisioningserver/tasks.py'
--- src/provisioningserver/tasks.py	2012-08-08 11:04:45 +0000
+++ src/provisioningserver/tasks.py	2012-08-09 04:22:20 +0000
@@ -29,6 +29,7 @@
 
 from celery.task import task
 from celeryconfig import DHCP_CONFIG_FILE
+from provisioningserver.auth import record_api_credentials
 from provisioningserver.dhcp import (
     config,
     leases,
@@ -47,6 +48,7 @@
 
 # For each item passed to refresh_secrets, a refresh function to give it to.
 refresh_functions = {
+    'api_credentials': record_api_credentials,
     'omapi_shared_key': leases.record_omapi_shared_key,
 }
 

=== added file 'src/provisioningserver/tests/test_auth.py'
--- src/provisioningserver/tests/test_auth.py	1970-01-01 00:00:00 +0000
+++ src/provisioningserver/tests/test_auth.py	2012-08-09 04:22:20 +0000
@@ -0,0 +1,48 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for management of node-group workers' API credentials."""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = []
+
+from maastesting.factory import factory
+from maastesting.testcase import TestCase
+from provisioningserver import auth
+
+
+def make_credentials():
+    """Produce a tuple of API credentials."""
+    return (
+        factory.make_name('consumer-key'),
+        factory.make_name('resource-token'),
+        factory.make_name('resource-secret'),
+        )
+
+
+def represent_credentials(credentials):
+    """Represent a tuple of API credentials as a credentials string."""
+    return ':'.join(credentials)
+
+
+class TestAuth(TestCase):
+
+    def test_record_api_credentials_records_credentials_string(self):
+        creds_string = represent_credentials(make_credentials())
+        auth.record_api_credentials(creds_string)
+        self.assertEqual(creds_string, auth.recorded_api_credentials)
+
+    def test_get_recorded_api_credentials_returns_credentials_as_tuple(self):
+        creds = make_credentials()
+        auth.record_api_credentials(represent_credentials(creds))
+        self.assertEqual(creds, auth.get_recorded_api_credentials())
+
+    def test_get_recorded_api_credentials_returns_None_without_creds(self):
+        auth.record_api_credentials(None)
+        self.assertIsNone(auth.get_recorded_api_credentials())

=== modified file 'src/provisioningserver/tests/test_tasks.py'
--- src/provisioningserver/tests/test_tasks.py	2012-08-08 05:54:08 +0000
+++ src/provisioningserver/tests/test_tasks.py	2012-08-09 04:22:20 +0000
@@ -23,6 +23,7 @@
 from maastesting.testcase import TestCase
 from netaddr import IPNetwork
 from provisioningserver import tasks
+from provisioningserver.auth import get_recorded_api_credentials
 from provisioningserver.dhcp import leases
 from provisioningserver.dns.config import (
     conf,
@@ -96,15 +97,24 @@
     def test_breaks_on_unknown_item(self):
         self.assertRaises(AssertionError, refresh_secrets, not_an_item=None)
 
+    def test_works_as_a_task(self):
+        self.assertTrue(refresh_secrets.delay().successful())
+
+    def test_updates_api_credentials(self):
+        credentials = (
+            factory.make_name('key'),
+            factory.make_name('token'),
+            factory.make_name('secret'),
+            )
+        refresh_secrets(api_credentials=':'.join(credentials))
+        self.assertEqual(credentials, get_recorded_api_credentials())
+
     def test_updates_omapi_shared_key(self):
         self.patch(leases, 'recorded_omapi_shared_key', None)
         key = factory.make_name('omapi-shared-key')
         refresh_secrets(omapi_shared_key=key)
         self.assertEqual(key, leases.recorded_omapi_shared_key)
 
-    def test_works_as_a_task(self):
-        self.assertTrue(refresh_secrets.delay().successful())
-
 
 class TestPowerTasks(TestCase):