Add magnum cluster config support

Add support to download cluster config from dashboard

Depends-On: https://review.opendev.org/c/openstack/python-magnumclient/+/890893
Change-Id: I533dbcefbe2828360b88291c0311cf3732050a68
This commit is contained in:
ricolin 2023-08-09 18:29:02 +08:00
parent 3954cea91c
commit 61307d6aa5
8 changed files with 215 additions and 0 deletions

View File

@ -21,6 +21,7 @@ from horizon import exceptions
from horizon.utils.memoized import memoized
from openstack_dashboard.api import base
from magnumclient.common import utils as client_utils
from magnumclient.v1 import certificates
from magnumclient.v1 import client as magnum_client
from magnumclient.v1 import cluster_templates
@ -197,6 +198,32 @@ def cluster_show(request, id):
return magnumclient(request).clusters.get(id)
def cluster_config(request, id):
cluster = magnumclient(request).clusters.get(id)
if (hasattr(cluster, 'api_address') and cluster.api_address is None):
LOG.debug(f"api_address for cluster {id} is not known yet.")
cluster_template = magnumclient(request).cluster_templates.get(
cluster.cluster_template_id
)
opts = {
'cluster_uuid': cluster.uuid,
}
tls = {}
if not cluster_template.tls_disabled:
tls = client_utils.generate_csr_and_key()
tls["ca"] = magnumclient(request).certificates.get(**opts).pem
opts["csr"] = tls.pop("csr")
tls["cert"] = magnumclient(request).certificates.create(**opts).pem
config = client_utils.config_cluster(
cluster, cluster_template, cfg_dir="", direct_output=True
)
result = {"cluster_config": config}
result.update(tls)
return result
def cluster_resize(request, cluster_id, node_count,
nodes_to_remove=None, nodegroup=None):

View File

@ -212,6 +212,17 @@ class Cluster(generic.View):
updated_cluster.to_dict())
@urls.register
class ClusterConfig(generic.View):
"""API for retrieving config for a single cluster"""
url_regex = r'container_infra/clusters/(?P<cluster_id>[^/]+)/config$'
@rest_utils.ajax()
def get(self, request, cluster_id):
"""Get config for a specific cluster"""
return magnum.cluster_config(request, cluster_id)
@urls.register
class ClusterResize(generic.View):

View File

@ -39,6 +39,7 @@
'horizon.dashboard.container-infra.clusters.show-certificate.service',
'horizon.dashboard.container-infra.clusters.sign-certificate.service',
'horizon.dashboard.container-infra.clusters.rotate-certificate.service',
'horizon.dashboard.container-infra.clusters.config.service',
'horizon.dashboard.container-infra.clusters.resourceType'
];
@ -52,6 +53,7 @@
showCertificateService,
signCertificateService,
rotateCertificateService,
getClusterConfigService,
resourceType) {
var clusterResourceType = registry.getResourceType(resourceType);
@ -97,6 +99,13 @@
text: gettext('Rotate Certificate')
}
})
.append({
id: 'getClusterConfigAction',
service: getClusterConfigService,
template: {
text: gettext('Get Cluster Config')
}
})
.append({
id: 'resizeClusterAction',
service: resizeClusterService,

View File

@ -60,6 +60,11 @@
expect(actionHasId(actions, 'deleteClusterAction')).toBe(true);
});
it('registers Get Cluster Config as an item action', function() {
var actions = registry.getResourceType('OS::Magnum::Cluster').itemActions;
expect(actionHasId(actions, 'getClusterConfigAction')).toBe(true);
});
function actionHasId(list, value) {
return list.filter(matchesId).length === 1;

View File

@ -0,0 +1,73 @@
/**
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
/**
* @ngdoc overview
* @name horizon.dashboard.container-infra.clusters.config.service
* @description Service for the container-infra cluster get config modal
*/
angular
.module('horizon.dashboard.container-infra.clusters')
.factory(
'horizon.dashboard.container-infra.clusters.config.service',
getClusterConfigService);
getClusterConfigService.$inject = [
'horizon.app.core.openstack-service-api.magnum',
'horizon.dashboard.container-infra.clusters.resourceType',
'horizon.framework.util.actions.action-result.service',
'horizon.framework.util.file.text-download',
'horizon.framework.util.q.extensions'
];
function getClusterConfigService(
magnum, resourceType, actionResult, textDownload, $qExtensions
) {
var service = {
initAction: initAction,
perform: perform,
allowed: allowed
};
return service;
//////////////
function initAction() {
}
function perform(selected) {
// get config
return magnum.getClusterConfig(selected.id).then(function(response) {
if ( response.data.key !== undefined ) {
textDownload.downloadTextFile(response.data.key, selected.name + "_key.pem");
textDownload.downloadTextFile(response.data.ca, selected.name + "_ca.pem");
textDownload.downloadTextFile(response.data.cert, selected.name + "_cert.pem");
}
textDownload.downloadTextFile(response.data.cluster_config, selected.name + "_config");
var result = actionResult.getActionResult()
.created(resourceType, selected.id);
return result.result;
});
}
function allowed() {
return $qExtensions.booleanAsPromise(true);
}
}
})();

View File

@ -0,0 +1,75 @@
/**
* (c) Copyright 2016 NEC Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
(function() {
'use strict';
describe('horizon.dashboard.container-infra.clusters.config.service', function() {
var $scope, service, selected, magnum, textDownload;
beforeEach(module('horizon.app.core'));
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.dashboard.container-infra.clusters'));
beforeEach(inject(function($injector) {
service = $injector.get(
'horizon.dashboard.container-infra.clusters.config.service');
magnum = $injector.get('horizon.app.core.openstack-service-api.magnum');
textDownload = $injector.get('horizon.framework.util.file.text-download');
spyOn(textDownload, 'downloadTextFile').and.returnValue(Promise.resolve(true));
$scope = $injector.get('$rootScope');
selected = {id: '1'};
}));
it('should check the policy', function() {
var allowed = service.allowed();
expect(allowed).toBeTruthy();
});
it('should get magnum.getClusterConfig', function() {
var returnValue = {data: {cluster_config: "config1"}};
spyOn(magnum, 'getClusterConfig').and.returnValue(Promise.resolve(returnValue));
service.initAction();
var promise = service.perform(selected);
promise.then(verifyContents);
$scope.$apply();
expect(magnum.getClusterConfig).toHaveBeenCalled();
function verifyContents (contents) {
expect(contents.created).toBeDefined();
expect(contents.failed).toEqual([]);
expect(textDownload.downloadTextFile.calls.count()).toBe(1);
}
});
it('should download', inject(function() {
var returnValue = {data: {key: "key1", cluster_config: "config1", ca: "ca1", cert: "cert1"}};
spyOn(magnum, 'getClusterConfig').and.returnValue(Promise.resolve(returnValue));
service.initAction();
var promise = service.perform(selected);
promise.then(verifyContents);
$scope.$apply();
expect(magnum.getClusterConfig).toHaveBeenCalled();
function verifyContents (contents) {
expect(contents.created).toBeDefined();
expect(contents.failed).toEqual([]);
expect(textDownload.downloadTextFile.calls.count()).toBe(4);
}
}));
});
})();

View File

@ -33,6 +33,7 @@
updateCluster: updateCluster,
upgradeCluster: upgradeCluster,
getCluster: getCluster,
getClusterConfig: getClusterConfig,
getClusters: getClusters,
getClusterNodes: getClusterNodes,
resizeCluster: resizeCluster,
@ -92,6 +93,13 @@
});
}
function getClusterConfig(id) {
return apiService.get('/api/container_infra/clusters/' + id + '/config')
.catch(function onError() {
toastService.add('error', gettext('Unable to retrieve the cluster config.'));
});
}
function getClusters() {
return apiService.get('/api/container_infra/clusters/')
.catch(function onError() {

View File

@ -62,6 +62,13 @@
"error": "Unable to retrieve the cluster.",
"testInput": ["123"]
},
{
"func": "getClusterConfig",
"method": "get",
"path": "/api/container_infra/clusters/123/config",
"error": "Unable to retrieve the cluster config.",
"testInput": ["123"]
},
{
"func": "getClusters",
"method": "get",