mirror of
https://github.com/portainer/portainer.git
synced 2025-08-09 15:55:23 +02:00
feature(kubeconfig): access to all kube environment contexts from within the Portainer UI [EE-1727] (#5966)
This commit is contained in:
parent
c0a4727114
commit
6be1ff4d9c
22 changed files with 404 additions and 434 deletions
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash-es';
|
||||
import { PortainerEndpointTypes } from '@/portainer/models/endpoint/models';
|
||||
|
||||
const ENDPOINTS_POLLING_INTERVAL = 30000; // in ms
|
||||
const ENDPOINTS_CACHE_SIZE = 100;
|
||||
|
@ -6,8 +7,10 @@ const ENDPOINTS_CACHE_SIZE = 100;
|
|||
angular.module('portainer.app').controller('EndpointListController', [
|
||||
'DatatableService',
|
||||
'PaginationService',
|
||||
'ModalService',
|
||||
'KubernetesConfigService',
|
||||
'Notifications',
|
||||
function EndpointListController(DatatableService, PaginationService, Notifications) {
|
||||
function EndpointListController(DatatableService, PaginationService, ModalService, KubernetesConfigService, Notifications) {
|
||||
this.state = {
|
||||
totalFilteredEndpoints: null,
|
||||
textFilter: '',
|
||||
|
@ -126,6 +129,50 @@ angular.module('portainer.app').controller('EndpointListController', [
|
|||
return status === 1 ? 'up' : 'down';
|
||||
}
|
||||
|
||||
this.showKubeconfigButton = function () {
|
||||
if (window.location.protocol !== 'https:') {
|
||||
return false;
|
||||
}
|
||||
return _.some(this.endpoints, (endpoint) => isKubernetesMode(endpoint));
|
||||
};
|
||||
|
||||
function isKubernetesMode(endpoint) {
|
||||
return [
|
||||
PortainerEndpointTypes.KubernetesLocalEnvironment,
|
||||
PortainerEndpointTypes.AgentOnKubernetesEnvironment,
|
||||
PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment,
|
||||
].includes(endpoint.Type);
|
||||
}
|
||||
|
||||
this.showKubeconfigModal = async function () {
|
||||
const kubeEnvironments = _.filter(this.endpoints, (endpoint) => isKubernetesMode(endpoint));
|
||||
const options = kubeEnvironments.map(function (environment) {
|
||||
return {
|
||||
text: `${environment.Name} (${environment.URL})`,
|
||||
value: environment.Id,
|
||||
};
|
||||
});
|
||||
|
||||
let expiryMessage = '';
|
||||
try {
|
||||
expiryMessage = await KubernetesConfigService.expiryMessage();
|
||||
} catch (e) {
|
||||
Notifications.error('Failed fetching kubeconfig expiry time', e);
|
||||
}
|
||||
|
||||
ModalService.confirmKubeconfigSelection(options, expiryMessage, async function (selectedEnvironmentIDs) {
|
||||
if (selectedEnvironmentIDs.length === 0) {
|
||||
Notifications.warning('No environment was selected');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await KubernetesConfigService.downloadKubeconfigFile(selectedEnvironmentIDs);
|
||||
} catch (e) {
|
||||
Notifications.error('Failed downloading kubeconfig file', e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.$onInit = function () {
|
||||
var textFilter = DatatableService.getDataTableTextFilters(this.tableKey);
|
||||
this.state.paginatedItemLimit = PaginationService.getPaginationLimit(this.tableKey);
|
||||
|
|
|
@ -12,6 +12,17 @@
|
|||
<button type="button" class="btn btn-sm btn-primary" ng-click="$ctrl.snapshotAction()" data-cy="home-refreshEndpointsButton">
|
||||
<i class="fa fa-sync space-right" aria-hidden="true"></i>Refresh
|
||||
</button>
|
||||
<button
|
||||
ng-if="$ctrl.showKubeconfigButton()"
|
||||
type="button"
|
||||
class="btn btn-sm btn-primary"
|
||||
ng-click="$ctrl.showKubeconfigModal()"
|
||||
analytics-on
|
||||
analytics-category="kubernetes"
|
||||
analytics-event="kubernetes-kubectl-kubeconfig-multi"
|
||||
>
|
||||
<i class="fas fa-download space-right"></i> kubeconfig
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="searchBar">
|
||||
|
|
|
@ -82,17 +82,20 @@ angular.module('portainer.app').factory('ModalService', [
|
|||
applyBoxCSS(box);
|
||||
}
|
||||
|
||||
function customPrompt(options, optionToggled) {
|
||||
function customCheckboxPrompt(options) {
|
||||
var box = bootbox.prompt({
|
||||
title: options.title,
|
||||
inputType: options.inputType,
|
||||
inputType: 'checkbox',
|
||||
inputOptions: options.inputOptions,
|
||||
buttons: confirmButtons(options),
|
||||
callback: options.callback,
|
||||
});
|
||||
applyBoxCSS(box);
|
||||
box.find('.bootbox-body').prepend('<p>' + options.message + '</p>');
|
||||
box.find('.bootbox-input-checkbox').prop('checked', optionToggled);
|
||||
box.find('.bootbox-input-checkbox').prop('checked', options.optionToggled);
|
||||
if (options.showCheck) {
|
||||
box.find('.bootbox-input-checkbox').addClass('visible');
|
||||
}
|
||||
}
|
||||
|
||||
service.confirmAccessControlUpdate = function (callback) {
|
||||
|
@ -231,28 +234,25 @@ angular.module('portainer.app').factory('ModalService', [
|
|||
};
|
||||
|
||||
service.confirmContainerRecreation = function (callback) {
|
||||
customPrompt(
|
||||
{
|
||||
title: 'Are you sure?',
|
||||
message:
|
||||
"You're about to re-create this container, any non-persisted data will be lost. This container will be removed and another one will be created using the same configuration.",
|
||||
inputType: 'checkbox',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Pull latest image<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Recreate',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
customCheckboxPrompt({
|
||||
title: 'Are you sure?',
|
||||
message:
|
||||
"You're about to re-create this container, any non-persisted data will be lost. This container will be removed and another one will be created using the same configuration.",
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Pull latest image<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Recreate',
|
||||
className: 'btn-danger',
|
||||
},
|
||||
callback: callback,
|
||||
},
|
||||
false
|
||||
);
|
||||
callback: callback,
|
||||
optionToggled: false,
|
||||
});
|
||||
};
|
||||
|
||||
service.confirmEndpointSnapshot = function (callback) {
|
||||
|
@ -285,27 +285,24 @@ angular.module('portainer.app').factory('ModalService', [
|
|||
|
||||
service.confirmServiceForceUpdate = function (message, callback) {
|
||||
message = $sanitize(message);
|
||||
customPrompt(
|
||||
{
|
||||
title: 'Are you sure ?',
|
||||
message: message,
|
||||
inputType: 'checkbox',
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Pull latest image version<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
customCheckboxPrompt({
|
||||
title: 'Are you sure ?',
|
||||
message: message,
|
||||
inputOptions: [
|
||||
{
|
||||
text: 'Pull latest image version<i></i>',
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
callback: callback,
|
||||
},
|
||||
false
|
||||
);
|
||||
callback: callback,
|
||||
optionToggled: false,
|
||||
});
|
||||
};
|
||||
|
||||
service.selectRegistry = function (options) {
|
||||
|
@ -319,6 +316,24 @@ angular.module('portainer.app').factory('ModalService', [
|
|||
applyBoxCSS(box);
|
||||
};
|
||||
|
||||
service.confirmKubeconfigSelection = function (options, expiryMessage, callback) {
|
||||
const message = 'Select the kubernetes environment(s) to add to the kubeconfig file.</br>' + expiryMessage;
|
||||
customCheckboxPrompt({
|
||||
title: 'Download kubeconfig file',
|
||||
message: $sanitize(message),
|
||||
inputOptions: options,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Download file',
|
||||
className: 'btn-primary',
|
||||
},
|
||||
},
|
||||
callback: callback,
|
||||
optionToggled: true,
|
||||
showCheck: true,
|
||||
});
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue