diff --git a/magnum_ui/api/magnum.py b/magnum_ui/api/magnum.py index 844df111..bb5fc2c7 100644 --- a/magnum_ui/api/magnum.py +++ b/magnum_ui/api/magnum.py @@ -40,6 +40,8 @@ CLUSTER_CREATE_ATTRS = ['name', 'baymodel_id', 'node_count', 'discovery_url', 'cluster_create_timeout', 'master_count'] +CERTIFICATE_CREATE_ATTRS = ['cluster_uuid', 'csr'] + @memoized def magnumclient(request): @@ -125,3 +127,18 @@ def cluster_list(request, limit=None, marker=None, sort_key=None, def cluster_show(request, id): return magnumclient(request).bays.get(id) + + +def certificate_create(request, **kwargs): + args = {} + for (key, value) in kwargs.items(): + if key in CERTIFICATE_CREATE_ATTRS: + args[key] = value + else: + raise exceptions.BadRequest( + "Key must be in %s" % ",".join(CERTIFICATE_CREATE_ATTRS)) + return magnumclient(request).certificates.create(**args) + + +def certificate_show(request, id): + return magnumclient(request).certificates.get(id) diff --git a/magnum_ui/api/rest/magnum.py b/magnum_ui/api/rest/magnum.py index fc75c461..d31cc972 100644 --- a/magnum_ui/api/rest/magnum.py +++ b/magnum_ui/api/rest/magnum.py @@ -123,3 +123,29 @@ class Clusters(generic.View): return rest_utils.CreatedResponse( '/api/container_infra/cluster/%s' % new_cluster.uuid, new_cluster.to_dict()) + + +@urls.register +class Certificates(generic.View): + """API for Magnum Certificates""" + url_regex = r'container_infra/certificates/(?P[^/]+)$' + + @rest_utils.ajax() + def get(self, request, cluster_id): + """Get a certificate from a cluster. + + Returns the CA.pem string on success + """ + ca = magnum.certificate_show(request, cluster_id) + return ca.to_dict() + + @rest_utils.ajax(data_required=True) + def post(self, request): + """Create a new Certificate. + + Returns the new Cert.pem string from csr for a cluster on success. + """ + new_cert = magnum.certificate_create(request, **request.DATA) + return rest_utils.CreatedResponse( + '/api/container_infra/certificates/', + new_cert.to_dict()) diff --git a/magnum_ui/static/dashboard/container-infra/magnum.service.js b/magnum_ui/static/dashboard/container-infra/magnum.service.js index 69865e1f..3a047f6a 100644 --- a/magnum_ui/static/dashboard/container-infra/magnum.service.js +++ b/magnum_ui/static/dashboard/container-infra/magnum.service.js @@ -38,13 +38,15 @@ getClusterTemplates: getClusterTemplates, deleteClusterTemplate: deleteClusterTemplate, deleteClusterTemplates: deleteClusterTemplates, + showCertificate: showCertificate, + signCertificate: signCertificate, }; return service; - ////////// + ////////////// // Clusters // - ////////// + ////////////// function createCluster(params) { return apiService.post('/api/container_infra/clusters/', params) @@ -83,14 +85,14 @@ }); } - /////////////// + ////////////////////// // ClusterTemplates // - /////////////// + ////////////////////// function createClusterTemplate(params) { return apiService.post('/api/container_infra/cluster_templates/', params) .error(function() { - toastService.add('error', gettext('Unable to create cluster template')); + toastService.add('error', gettext('Unable to create cluster template.')); }); } @@ -123,5 +125,23 @@ toastService.add('error', gettext('Unable to delete the cluster templates.')); }) } + + ////////////////// + // Certificates // + ////////////////// + + function signCertificate(params) { + return apiService.post('/api/container_infra/certificates/', params) + .error(function() { + toastService.add('error', gettext('Unable to sign certificate.')); + }); + } + + function showCertificate(id) { + return apiService.get('/api/container_infra/certificates/' + id) + .error(function() { + toastService.add('error', gettext('Unable to retrieve the certificate.')); + }); + } } }()); diff --git a/magnum_ui/test/api_tests/rest_api_tests.py b/magnum_ui/test/api_tests/rest_api_tests.py index e42fc8e0..de3399cd 100644 --- a/magnum_ui/test/api_tests/rest_api_tests.py +++ b/magnum_ui/test/api_tests/rest_api_tests.py @@ -105,6 +105,26 @@ class MagnumRestTestCase(test.TestCase): request, u'bay_id') + # Certificates + @mock.patch.object(magnum, 'magnum') + def test_certificate_create(self, client): + test_certificates = mock_resource(TEST.certificates.list()) + test_certificate = test_certificates[0] + test_body = json.dumps(test_certificate.to_dict()) + request = self.mock_rest_request(body=test_body) + + test_res_list = mock_resource(TEST.certificate_res_list.list()) + test_res = test_res_list[0] + client.certificate_create.return_value = test_res + + response = magnum.Certificates().post(request) + res_body = json.loads(response.content) + + self.assertStatusCode(response, 201) + self.assertEqual(res_body['pem'], test_res.to_dict()['pem']) + client.certificate_create.assert_called_once_with( + request, **test_certificate.to_dict()) + def mock_resource(resource): """Utility function to make mocking more DRY""" diff --git a/magnum_ui/test/test_data.py b/magnum_ui/test/test_data.py index c790b5ae..5159cbde 100644 --- a/magnum_ui/test/test_data.py +++ b/magnum_ui/test/test_data.py @@ -19,6 +19,8 @@ def data(TEST): # Test Data Container in Horizon TEST.cluster_templates = utils.TestDataContainer() TEST.clusters = utils.TestDataContainer() + TEST.certificates = utils.TestDataContainer() + TEST.certificate_res_list = utils.TestDataContainer() # Cluster Templates cluster_template_dict_1 = {"uuid": 1, @@ -56,3 +58,22 @@ def data(TEST): "timeout": 0} TEST.clusters.add(cluster_dict_1) + + # Certificates + certificate_1 = {"cluster_uuid": 1, + "csr": "kore-ya-kono\n" + "yuku-mo-kaheru-mo\n" + "wakarete-ha\n" + "shiru-mo-shiranu-mo\n" + "afusaka-no-seki"} + + TEST.certificates.add(certificate_1) + + certificate_res = {"cluster_uuid": 1, + "pem": "wata-no-hara\n" + "yasoshima-kakete\n" + "kogi-idenu-to\n" + "hito-niwa-tsugeyo\n" + "ama-no-tsuri-fune"} + + TEST.certificate_res_list.add(certificate_res)