1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 12:25:22 +02:00

feat(config): separate configmaps and secrets [EE-5078] (#9029)

This commit is contained in:
Ali 2023-06-12 09:46:48 +12:00 committed by GitHub
parent 4a331b71e1
commit d7fc2046d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
102 changed files with 2845 additions and 665 deletions

View file

@ -0,0 +1,124 @@
<page-header
ng-if="ctrl.state.viewReady"
title="'Create ConfigMap'"
breadcrumbs="[{ label:'ConfigMaps and Secrets', link:'kubernetes.configurations', linkParams:{ tab: 'configmaps' } }, 'Create a ConfigMap']"
reload="true"
></page-header>
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
<div class="row">
<div class="col-xs-12">
<rd-widget>
<rd-widget-body>
<form class="form-horizontal" name="kubernetesConfigurationCreationForm" autocomplete="off">
<!-- resource-pool -->
<div class="form-group" ng-if="ctrl.formValues.ResourcePool">
<label for="resource-pool-selector" class="col-sm-3 col-lg-2 control-label text-left">Namespace</label>
<div class="col-sm-8 col-lg-9">
<select
class="form-control"
id="resource-pool-selector"
ng-model="ctrl.formValues.ResourcePool"
ng-options="resourcePool.Namespace.Name for resourcePool in ctrl.resourcePools"
ng-change="ctrl.onResourcePoolSelectionChange()"
data-cy="k8sConfigCreate-namespaceDropdown"
></select>
</div>
</div>
<div class="form-group" ng-if="ctrl.state.resourcePoolHasQuota && ctrl.resourceQuotaCapacityExceeded() && ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-warning vertical-center">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
This namespace has exhausted its resource capacity and you will not be able to deploy the configuration. Contact your administrator to expand the capacity of the
namespace.
</div>
</div>
<div class="form-group" ng-if="!ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-warning vertical-center">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div>
</div>
<!-- !resource-pool -->
<!-- name -->
<div class="form-group">
<label for="configuration_name" class="col-sm-3 col-lg-2 control-label required text-left">Name</label>
<div class="col-sm-8 col-lg-9 mb-0">
<input
type="text"
class="form-control"
name="configuration_name"
ng-model="ctrl.formValues.Name"
ng-pattern="/^[a-z0-9]([a-z0-9-.]{0,61}[a-z0-9])?$/"
ng-change="ctrl.onChangeName()"
placeholder="my-configmap"
auto-focus
required
data-cy="k8sConfigCreate-nameInput"
/>
<div ng-show="kubernetesConfigurationCreationForm.configuration_name.$invalid || ctrl.state.alreadyExist">
<div class="help-block small text-warning">
<div ng-messages="kubernetesConfigurationCreationForm.configuration_name.$error">
<p ng-message="required" class="vertical-center"><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> This field is required.</p>
<p ng-message="pattern" class="vertical-center"
><pr-icon icon="'alert-triangle'" mode="'warning'" class="vertical-center"></pr-icon> This field must consist of lower case alphanumeric characters, '-' or
'.', and contain at most 63 characters, and must start and end with an alphanumeric character.</p
>
</div>
<p ng-if="ctrl.state.alreadyExist" class="vertical-center"
><pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> A configuration with the same name already exists inside the selected namespace.</p
>
</div>
</div>
</div>
</div>
<!-- !name -->
<div class="col-sm-12 !p-0">
<annotations-be-teaser></annotations-be-teaser>
</div>
<div ng-if="ctrl.formValues.ResourcePool">
<kubernetes-configuration-data
ng-if="ctrl.formValues"
form-values="ctrl.formValues"
is-docker-config="ctrl.state.isDockerConfig"
is-valid="ctrl.state.isDataValid"
on-change-validation="ctrl.isFormValid()"
is-creation="true"
is-editor-dirty="ctrl.state.isEditorDirty"
></kubernetes-configuration-data>
</div>
<!-- summary -->
<kubernetes-summary-view
ng-if="!(!kubernetesConfigurationCreationForm.$valid || !ctrl.isFormValid() || ctrl.state.actionInProgress)"
form-values="ctrl.formValues"
></kubernetes-summary-view>
<!-- actions -->
<div class="col-sm-12 form-section-title" style="margin-top: 10px"> Actions </div>
<div class="form-group">
<div class="col-sm-12">
<button
type="button"
class="btn btn-primary btn-sm !ml-0"
ng-disabled="!kubernetesConfigurationCreationForm.$valid || !ctrl.isFormValid() || ctrl.state.actionInProgress || !ctrl.formValues.ResourcePool"
ng-click="ctrl.createConfiguration()"
button-spinner="ctrl.state.actionInProgress"
data-cy="k8sConfigCreate-CreateConfigButton"
>
<span ng-hide="ctrl.state.actionInProgress">Create {{ ctrl.formValues.Kind | kubernetesConfigurationKindText }}</span>
<span ng-show="ctrl.state.actionInProgress">Creation in progress...</span>
</button>
</div>
</div>
<!-- !actions -->
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
</div>

View file

@ -0,0 +1,5 @@
angular.module('portainer.kubernetes').component('kubernetesCreateConfigMapView', {
templateUrl: './createConfigMap.html',
controller: 'KubernetesCreateConfigMapController',
controllerAs: 'ctrl',
});

View file

@ -0,0 +1,170 @@
import angular from 'angular';
import _ from 'lodash-es';
import { KubernetesConfigurationFormValues, KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
import { getServiceAccounts } from 'Kubernetes/rest/serviceAccount';
import { typeOptions } from '@/react/kubernetes/configs/CreateView/options';
import { confirmWebEditorDiscard } from '@@/modals/confirm';
import { isConfigurationFormValid } from '../../validation';
class KubernetesCreateConfigMapController {
/* @ngInject */
constructor($async, $state, $scope, $window, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService, EndpointProvider) {
this.$async = $async;
this.$state = $state;
this.$scope = $scope;
this.$window = $window;
this.EndpointProvider = EndpointProvider;
this.Notifications = Notifications;
this.Authentication = Authentication;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
this.typeOptions = typeOptions;
this.onInit = this.onInit.bind(this);
this.createConfigurationAsync = this.createConfigurationAsync.bind(this);
this.getConfigurationsAsync = this.getConfigurationsAsync.bind(this);
this.onResourcePoolSelectionChangeAsync = this.onResourcePoolSelectionChangeAsync.bind(this);
}
onChangeName() {
const filteredConfigurations = _.filter(
this.configurations,
(config) => config.Namespace === this.formValues.ResourcePool.Namespace.Name && config.Kind === this.formValues.Kind
);
this.state.alreadyExist = _.find(filteredConfigurations, (config) => config.Name === this.formValues.Name) !== undefined;
}
async onResourcePoolSelectionChangeAsync() {
try {
this.onChangeName();
this.availableServiceAccounts = await getServiceAccounts(this.environmentId, this.formValues.ResourcePool.Namespace.Name);
this.formValues.ServiceAccountName = this.availableServiceAccounts.length > 0 ? this.availableServiceAccounts[0].metadata.name : '';
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load service accounts');
}
}
onResourcePoolSelectionChange() {
this.$async(this.onResourcePoolSelectionChangeAsync);
}
addRequiredKeysToForm(keys) {
// remove data entries that have an empty value
this.formValues.Data = this.formValues.Data.filter((entry) => entry.Value);
keys.forEach((key) => {
// if the key doesn't exist on the form, add a new formValues.Data entry
if (!this.formValues.Data.some((data) => data.Key === key)) {
this.formValues.Data.push(new KubernetesConfigurationFormValuesEntry());
const index = this.formValues.Data.length - 1;
this.formValues.Data[index].Key = key;
}
});
}
isFormValid() {
const [isValid] = isConfigurationFormValid(this.state.alreadyExist, this.state.isDataValid, this.formValues);
return isValid;
}
async createConfigurationAsync() {
try {
this.state.actionInProgress = true;
this.formValues.ConfigurationOwner = this.Authentication.getUserDetails().username;
if (!this.formValues.IsSimple) {
this.formValues.Data = KubernetesConfigurationHelper.parseYaml(this.formValues);
}
await this.KubernetesConfigurationService.create(this.formValues);
this.Notifications.success('Success', `ConfigMap successfully created`);
this.state.isEditorDirty = false;
this.$state.go('kubernetes.configurations', { tab: 'configmaps' });
} catch (err) {
this.Notifications.error('Failure', err, `Unable to create ConfigMap`);
} finally {
this.state.actionInProgress = false;
}
}
createConfiguration() {
return this.$async(this.createConfigurationAsync);
}
async getConfigurationsAsync() {
try {
this.configurations = await this.KubernetesConfigurationService.get();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve ConfigMaps');
}
}
getConfigurations() {
return this.$async(this.getConfigurationsAsync);
}
async uiCanExit() {
if (!this.formValues.IsSimple && this.formValues.DataYaml && this.state.isEditorDirty) {
return confirmWebEditorDiscard();
}
}
async onInit() {
this.state = {
actionInProgress: false,
viewReady: false,
alreadyExist: false,
isDataValid: true,
isEditorDirty: false,
isDockerConfig: false,
};
this.formValues = new KubernetesConfigurationFormValues();
this.formValues.Kind = KubernetesConfigurationKinds.CONFIGMAP;
this.formValues.Data = [new KubernetesConfigurationFormValuesEntry()];
try {
const resourcePools = await this.KubernetesResourcePoolService.get();
this.resourcePools = _.filter(
resourcePools,
(resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && resourcePool.Namespace.Status === 'Active'
);
this.formValues.ResourcePool = this.resourcePools[0];
if (!this.formValues.ResourcePool) {
return;
}
await this.getConfigurations();
this.environmentId = this.EndpointProvider.endpointID();
this.availableServiceAccounts = await getServiceAccounts(this.environmentId, this.resourcePools[0].Namespace.Name);
this.formValues.ServiceAccountName = this.availableServiceAccounts.length > 0 ? this.availableServiceAccounts[0].metadata.name : '';
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load view data');
} finally {
this.state.viewReady = true;
}
this.$window.onbeforeunload = () => {
if (!this.formValues.IsSimple && this.formValues.DataYaml && this.state.isEditorDirty) {
return '';
}
};
}
$onDestroy() {
this.state.isEditorDirty = false;
}
$onInit() {
return this.$async(this.onInit);
}
}
export default KubernetesCreateConfigMapController;
angular.module('portainer.kubernetes').controller('KubernetesCreateConfigMapController', KubernetesCreateConfigMapController);

View file

@ -0,0 +1,173 @@
<page-header
ng-if="ctrl.state.viewReady"
title="'ConfigMap details'"
breadcrumbs="[
{ label:'Namespaces', link:'kubernetes.resourcePools' },
{
label:ctrl.configuration.Namespace,
link: 'kubernetes.resourcePools.resourcePool',
linkParams:{ id: ctrl.configuration.Namespace }
},
{ label:'ConfigMaps and Secrets', link:'kubernetes.configurations', linkParams:{ tab: 'configmaps' } },
ctrl.configuration.Name,
]"
reload="true"
>
</page-header>
<kubernetes-view-loading view-ready="ctrl.state.viewReady"></kubernetes-view-loading>
<div ng-if="ctrl.state.viewReady">
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body classes="no-padding">
<uib-tabset active="ctrl.state.activeTab" justified="true" type="pills">
<uib-tab index="0" classes="btn-sm" select="ctrl.selectTab(0)" data-cy="k8sConfigDetail-configTab">
<uib-tab-heading>
<pr-icon icon="'file-code'"></pr-icon>
ConfigMap
</uib-tab-heading>
<div style="padding: 20px">
<table class="table" data-cy="k8sConfigDetail-configTable">
<tbody>
<tr>
<td class="w-[40%] !border-none !pl-0">Name</td>
<td class="!border-none">
{{ ctrl.configuration.Name }}
</td>
</tr>
<tr>
<td class="!pl-0">Namespace</td>
<td>
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: ctrl.configuration.Namespace })">{{ ctrl.configuration.Namespace }}</a>
<span style="margin-left: 5px" class="label label-info image-tag" ng-if="ctrl.isSystemNamespace()">system</span>
</td>
</tr>
</tbody>
</table>
</div>
</uib-tab>
<uib-tab index="1" classes="btn-sm" select="ctrl.selectTab(1)" data-cy="k8sConfigDetail-eventsTab">
<uib-tab-heading>
<pr-icon icon="'history'"></pr-icon>
Events
<div ng-if="ctrl.hasEventWarnings()">
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon>
{{ ctrl.state.eventWarningCount }} warning(s)
</div>
</uib-tab-heading>
<kubernetes-events-datatable
title-text="Events"
title-icon="history"
dataset="ctrl.events"
table-key="kubernetes.configuration.events"
order-by="Date"
reverse-order="true"
loading="ctrl.state.eventsLoading"
refresh-callback="ctrl.getEvents"
>
</kubernetes-events-datatable>
</uib-tab>
<uib-tab index="2" ng-if="ctrl.configuration.Yaml" classes="btn-sm" select="ctrl.showEditor()" data-cy="k8sConfigDetail-yamlTab">
<uib-tab-heading>
<pr-icon icon="'code'"></pr-icon>
YAML
</uib-tab-heading>
<div class="px-5 !pt-5" ng-if="ctrl.state.showEditorTab">
<kubernetes-yaml-inspector key="configuration-yaml" data="ctrl.configuration.Yaml"></kubernetes-yaml-inspector>
</div>
</uib-tab>
</uib-tabset>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body>
<form ng-if="!ctrl.isSystemConfig()" class="form-horizontal" name="kubernetesConfigurationCreationForm" autocomplete="off">
<div class="col-sm-12 !p-0">
<annotations-be-teaser></annotations-be-teaser>
</div>
<kubernetes-configuration-data
ng-if="ctrl.formValues"
form-values="ctrl.formValues"
is-docker-config="false"
is-valid="ctrl.state.isDataValid"
on-change-validation="ctrl.isFormValid()"
is-creation="false"
is-editor-dirty="ctrl.state.isEditorDirty"
></kubernetes-configuration-data>
<!-- summary -->
<kubernetes-summary-view
ng-if="!(!ctrl.isFormValid() || !kubernetesConfigurationCreationForm.$valid || ctrl.state.actionInProgress)"
form-values="ctrl.formValues"
></kubernetes-summary-view>
<!-- actions -->
<div class="col-sm-12 form-section-title" style="margin-top: 10px"> Actions </div>
<div class="form-group">
<div class="col-sm-12">
<button
type="button"
class="btn btn-primary btn-sm !ml-0"
ng-disabled="!ctrl.isFormValid() || !kubernetesConfigurationCreationForm.$valid || ctrl.state.actionInProgress"
ng-click="ctrl.updateConfiguration()"
button-spinner="ctrl.state.actionInProgress"
data-cy="k8sConfigDetail-updateConfig"
>
<span ng-hide="ctrl.state.actionInProgress">Update {{ ctrl.configuration.Kind | kubernetesConfigurationKindText }}</span>
<span ng-show="ctrl.state.actionInProgress">Update in progress...</span>
</button>
</div>
</div>
<!-- !actions -->
</form>
<div ng-if="ctrl.isSystemConfig()">
<div class="col-sm-12 form-section-title" style="margin-top: 10px"> Data </div>
<table class="table">
<tbody>
<tr class="text-muted">
<td style="width: 10%; border-top: none">Key</td>
<td style="width: 90%; border-top: none">Value</td>
</tr>
<tr ng-repeat="item in ctrl.formValues.Data track by $index">
<td>{{ item.Key }}</td>
<td>
<div style="white-space: pre-wrap">{{ item.Value }}</div>
<div style="margin-top: 2px">
<span class="btn btn-primary btn-xs" ng-click="ctrl.copyConfigurationValue($index)"> <pr-icon icon="'copy'" class-name="'mr-0.5'"></pr-icon>Copy </span>
<span id="copyValueNotification_{{ $index }}" style="display: none; color: #23ae89; margin-left: 5px" class="small">
<pr-icon icon="'check'"></pr-icon> copied
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row" ng-if="ctrl.configuration.Used">
<div class="col-sm-12">
<kubernetes-integrated-applications-datatable
dataset="ctrl.configuration.Applications"
table-key="kubernetes.configurations.applications"
order-by="Name"
refresh-callback="ctrl.getApplications"
title-text="Applications using this ConfigMap"
title-icon="svg-laptopcode"
>
</kubernetes-integrated-applications-datatable>
</div>
</div>
</div>

View file

@ -0,0 +1,8 @@
angular.module('portainer.kubernetes').component('kubernetesConfigMapView', {
templateUrl: './configMap.html',
controller: 'KubernetesConfigMapController',
controllerAs: 'ctrl',
bindings: {
$transition$: '<',
},
});

View file

@ -0,0 +1,304 @@
import angular from 'angular';
import _ from 'lodash-es';
import { KubernetesConfigurationFormValues } from 'Kubernetes/models/configuration/formvalues';
import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
import { pluralize } from '@/portainer/helpers/strings';
import { confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confirm';
import { isConfigurationFormValid } from '../../validation';
class KubernetesConfigMapController {
/* @ngInject */
constructor(
$async,
$state,
$window,
clipboard,
EndpointProvider,
Notifications,
LocalStorage,
Authentication,
KubernetesConfigurationService,
KubernetesConfigMapService,
KubernetesResourcePoolService,
KubernetesApplicationService,
KubernetesEventService
) {
this.$async = $async;
this.$state = $state;
this.$window = $window;
this.clipboard = clipboard;
this.EndpointProvider = EndpointProvider;
this.Notifications = Notifications;
this.LocalStorage = LocalStorage;
this.Authentication = Authentication;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesConfigMapService = KubernetesConfigMapService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesEventService = KubernetesEventService;
this.KubernetesConfigurationKinds = KubernetesConfigurationKinds;
this.onInit = this.onInit.bind(this);
this.getConfigurationAsync = this.getConfigurationAsync.bind(this);
this.getEvents = this.getEvents.bind(this);
this.getEventsAsync = this.getEventsAsync.bind(this);
this.getApplications = this.getApplications.bind(this);
this.getApplicationsAsync = this.getApplicationsAsync.bind(this);
this.getConfigurationsAsync = this.getConfigurationsAsync.bind(this);
this.updateConfiguration = this.updateConfiguration.bind(this);
this.updateConfigurationAsync = this.updateConfigurationAsync.bind(this);
}
isSystemNamespace() {
return KubernetesNamespaceHelper.isSystemNamespace(this.configuration.Namespace);
}
isSystemConfig() {
return this.isSystemNamespace();
}
selectTab(index) {
this.LocalStorage.storeActiveTab('configuration', index);
}
showEditor() {
this.state.showEditorTab = true;
this.selectTab(2);
}
copyConfigurationValue(idx) {
this.clipboard.copyText(this.formValues.Data[idx].Value);
$('#copyValueNotification_' + idx)
.show()
.fadeOut(2500);
}
isFormValid() {
const [isValid] = isConfigurationFormValid(this.state.alreadyExist, this.state.isDataValid, this.formValues);
return isValid;
}
// TODO: refactor
// It looks like we're still doing a create/delete process but we've decided to get rid of this
// approach.
async updateConfigurationAsync() {
try {
this.state.actionInProgress = true;
if (
this.formValues.Kind !== this.configuration.Kind ||
this.formValues.ResourcePool.Namespace.Name !== this.configuration.Namespace ||
this.formValues.Name !== this.configuration.Name
) {
await this.KubernetesConfigurationService.create(this.formValues);
await this.KubernetesConfigurationService.delete(this.configuration);
this.Notifications.success('Success', `ConfigMap successfully updated`);
this.$state.go(
'kubernetes.configurations.configmap',
{
namespace: this.formValues.ResourcePool.Namespace.Name,
name: this.formValues.Name,
},
{ reload: true }
);
} else {
await this.KubernetesConfigurationService.update(this.formValues, this.configuration);
this.Notifications.success('Success', `ConfigMap successfully updated`);
this.$state.reload(this.$state.current);
}
} catch (err) {
this.Notifications.error('Failure', err, `Unable to update ConfigMap`);
} finally {
this.state.actionInProgress = false;
}
}
updateConfiguration() {
if (this.configuration.Used) {
confirmUpdate(
`The changes will be propagated to ${this.configuration.Applications.length} running ${pluralize(
this.configuration.Applications.length,
'application'
)}. Are you sure you want to update this ConfigMap?`,
(confirmed) => {
if (confirmed) {
return this.$async(this.updateConfigurationAsync);
}
}
);
} else {
return this.$async(this.updateConfigurationAsync);
}
}
async getConfigurationAsync() {
try {
this.state.configurationLoading = true;
const name = this.$transition$.params().name;
const namespace = this.$transition$.params().namespace;
try {
const configMap = await this.KubernetesConfigMapService.get(namespace, name);
this.configuration = KubernetesConfigurationConverter.configMapToConfiguration(configMap);
this.formValues.Data = configMap.Data;
} catch (err) {
if (err.status === 403) {
this.$state.go('kubernetes.configurations', { tab: 'configmaps' });
throw new Error('Not authorized to edit ConfigMap');
}
}
this.formValues.ResourcePool = _.find(this.resourcePools, (resourcePool) => resourcePool.Namespace.Name === this.configuration.Namespace);
this.formValues.Id = this.configuration.Id;
this.formValues.Name = this.configuration.Name;
this.formValues.Type = this.configuration.Type;
this.formValues.Kind = this.configuration.Kind;
this.oldDataYaml = this.formValues.DataYaml;
return this.configuration;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve ConfigMap');
} finally {
this.state.configurationLoading = false;
}
}
getConfiguration() {
return this.$async(this.getConfigurationAsync);
}
async getApplicationsAsync(namespace) {
try {
this.state.applicationsLoading = true;
const applications = await this.KubernetesApplicationService.get(namespace);
this.configuration.Applications = KubernetesConfigurationHelper.getUsingApplications(this.configuration, applications);
KubernetesConfigurationHelper.setConfigurationUsed(this.configuration);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve applications');
} finally {
this.state.applicationsLoading = false;
}
}
getApplications(namespace) {
return this.$async(this.getApplicationsAsync, namespace);
}
hasEventWarnings() {
return this.state.eventWarningCount;
}
async getEventsAsync(namespace) {
try {
this.state.eventsLoading = true;
this.events = await this.KubernetesEventService.get(namespace);
this.events = _.filter(this.events, (event) => event.Involved.uid === this.configuration.Id);
this.state.eventWarningCount = KubernetesEventHelper.warningCount(this.events);
} catch (err) {
this.Notifications('Failure', err, 'Unable to retrieve events');
} finally {
this.state.eventsLoading = false;
}
}
getEvents(namespace) {
return this.$async(this.getEventsAsync, namespace);
}
async getConfigurationsAsync() {
try {
this.configurations = await this.KubernetesConfigurationService.get();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve configurations');
}
}
getConfigurations() {
return this.$async(this.getConfigurationsAsync);
}
tagUsedDataKeys() {
const configName = this.$transition$.params().name;
const usedDataKeys = _.uniq(
this.configuration.Applications.flatMap((app) =>
app.Env.filter((e) => e.valueFrom && e.valueFrom.configMapKeyRef && e.valueFrom.configMapKeyRef.name === configName).map((e) => e.name)
)
);
this.formValues.Data = this.formValues.Data.map((variable) => {
if (!usedDataKeys.includes(variable.Key)) {
return variable;
}
return { ...variable, Used: true };
});
}
async uiCanExit() {
if (!this.formValues.IsSimple && this.formValues.DataYaml !== this.oldDataYaml && this.state.isEditorDirty) {
return confirmWebEditorDiscard();
}
}
async onInit() {
try {
this.state = {
actionInProgress: false,
configurationLoading: true,
applicationsLoading: true,
eventsLoading: true,
showEditorTab: false,
viewReady: false,
eventWarningCount: 0,
activeTab: 0,
currentName: this.$state.$current.name,
isDataValid: true,
isEditorDirty: false,
};
this.state.activeTab = this.LocalStorage.getActiveTab('configuration');
this.formValues = new KubernetesConfigurationFormValues();
this.resourcePools = await this.KubernetesResourcePoolService.get();
const configuration = await this.getConfiguration();
if (configuration) {
await this.getApplications(this.configuration.Namespace);
await this.getEvents(this.configuration.Namespace);
await this.getConfigurations();
}
this.tagUsedDataKeys();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load view data');
} finally {
this.state.viewReady = true;
}
this.$window.onbeforeunload = () => {
if (!this.formValues.IsSimple && this.formValues.DataYaml !== this.oldDataYaml && this.state.isEditorDirty) {
return '';
}
};
}
$onInit() {
return this.$async(this.onInit);
}
$onDestroy() {
if (this.state.currentName !== this.$state.$current.name) {
this.LocalStorage.storeActiveTab('configuration', 0);
}
this.state.isEditorDirty = false;
}
}
export default KubernetesConfigMapController;
angular.module('portainer.kubernetes').controller('KubernetesConfigMapController', KubernetesConfigMapController);