1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 13:55:21 +02:00

feat(k8s/resource-pool): add the ability to mark/unmark resource pool as system (#5360)

* feat(k8s/resource-pool): add the ability to mark/unmark resource pool as system

fix(kube/ns): check label to see if namespace is system

refactor(k8s/namespaces): rename variables

feat(kubernetes): toggle system state in the server (#5361)

fix(app/resource-pool): UI fixes

feat(app/resource-pool): add confirmation modal when unamrking system namespace

* refactor(app): review changes

* feat(app/namespaces): introduce store to retrieve namespace system status without changing all the kubernetes models

refactor(app/namespaces): remove unused code first introduced for system tagging

fix(app/namespaces): cache namespaces to retrieve system status regardless of namespace reference format

refactor(app): migrate namespace store from helper to a separate singleton

refactor(app): remove KubernetesNamespaceHelper from DI cycle

* refactor(app): normalize usage of KubernetesNamespaceHelper functions

* refactor(app/k8s): change namespace store to functions instead of class

Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
This commit is contained in:
Chaim Lev-Ari 2021-08-26 17:00:59 +03:00 committed by GitHub
parent 5ab98f41f1
commit 1830a80a61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 675 additions and 154 deletions

View file

@ -28,6 +28,7 @@ import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceRese
import { KubernetesServiceTypes } from 'Kubernetes/models/service/models';
import KubernetesApplicationHelper from 'Kubernetes/helpers/application/index';
import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
import { KubernetesNodeHelper } from 'Kubernetes/node/helper';
class KubernetesCreateApplicationController {
@ -47,7 +48,6 @@ class KubernetesCreateApplicationController {
KubernetesNodeService,
KubernetesIngressService,
KubernetesPersistentVolumeClaimService,
KubernetesNamespaceHelper,
KubernetesVolumeService,
RegistryService
) {
@ -64,7 +64,6 @@ class KubernetesCreateApplicationController {
this.KubernetesVolumeService = KubernetesVolumeService;
this.KubernetesIngressService = KubernetesIngressService;
this.KubernetesPersistentVolumeClaimService = KubernetesPersistentVolumeClaimService;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.RegistryService = RegistryService;
this.ApplicationDeploymentTypes = KubernetesApplicationDeploymentTypes;
@ -955,7 +954,7 @@ class KubernetesCreateApplicationController {
]);
this.ingresses = ingresses;
this.resourcePools = _.filter(resourcePools, (resourcePool) => !this.KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
this.resourcePools = _.filter(resourcePools, (resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
this.formValues.ResourcePool = this.resourcePools[0];
if (!this.formValues.ResourcePool) {
return;

View file

@ -32,7 +32,7 @@
<td>Namespace</td>
<td data-cy="k8sAppDetail-resourcePoolName">
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: ctrl.application.ResourcePool })">{{ ctrl.application.ResourcePool }}</a>
<span style="margin-left: 5px;" class="label label-info image-tag" ng-if="ctrl.isSystemNamespace(item)">system</span>
<span style="margin-left: 5px;" class="label label-info image-tag" ng-if="ctrl.isSystemNamespace()">system</span>
</td>
</tr>
<tr>

View file

@ -7,6 +7,7 @@ import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
import { KubernetesServiceTypes } from 'Kubernetes/models/service/models';
import { KubernetesPodNodeAffinityNodeSelectorRequirementOperators } from 'Kubernetes/pod/models';
import { KubernetesPodContainerTypes } from 'Kubernetes/pod/models/index';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
function computeTolerations(nodes, application) {
const pod = application.Pods[0];
@ -106,7 +107,6 @@ class KubernetesApplicationController {
KubernetesStackService,
KubernetesPodService,
KubernetesNodeService,
KubernetesNamespaceHelper,
EndpointProvider
) {
this.$async = $async;
@ -122,8 +122,6 @@ class KubernetesApplicationController {
this.KubernetesPodService = KubernetesPodService;
this.KubernetesNodeService = KubernetesNodeService;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.KubernetesApplicationDeploymentTypes = KubernetesApplicationDeploymentTypes;
this.KubernetesApplicationTypes = KubernetesApplicationTypes;
this.EndpointProvider = EndpointProvider;
@ -153,7 +151,7 @@ class KubernetesApplicationController {
}
isSystemNamespace() {
return this.KubernetesNamespaceHelper.isSystemNamespace(this.application.ResourcePool);
return KubernetesNamespaceHelper.isSystemNamespace(this.application.ResourcePool);
}
isExternalApplication() {

View file

@ -3,10 +3,11 @@ import _ from 'lodash-es';
import { KubernetesConfigurationFormValues, KubernetesConfigurationFormValuesEntry } from 'Kubernetes/models/configuration/formvalues';
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
class KubernetesCreateConfigurationController {
/* @ngInject */
constructor($async, $state, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService, KubernetesNamespaceHelper) {
constructor($async, $state, $window, ModalService, Notifications, Authentication, KubernetesConfigurationService, KubernetesResourcePoolService) {
this.$async = $async;
this.$state = $state;
this.$window = $window;
@ -16,7 +17,6 @@ class KubernetesCreateConfigurationController {
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesConfigurationTypes = KubernetesConfigurationTypes;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.onInit = this.onInit.bind(this);
this.createConfigurationAsync = this.createConfigurationAsync.bind(this);
@ -94,7 +94,7 @@ class KubernetesCreateConfigurationController {
try {
const resourcePools = await this.KubernetesResourcePoolService.get();
this.resourcePools = _.filter(resourcePools, (resourcePool) => !this.KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
this.resourcePools = _.filter(resourcePools, (resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name));
this.formValues.ResourcePool = this.resourcePools[0];
await this.getConfigurations();

View file

@ -1,10 +1,12 @@
import angular from 'angular';
import _ from 'lodash-es';
import { KubernetesConfigurationFormValues } from 'Kubernetes/models/configuration/formvalues';
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
import _ from 'lodash-es';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
class KubernetesConfigurationController {
/* @ngInject */
@ -21,8 +23,7 @@ class KubernetesConfigurationController {
KubernetesResourcePoolService,
ModalService,
KubernetesApplicationService,
KubernetesEventService,
KubernetesNamespaceHelper
KubernetesEventService
) {
this.$async = $async;
this.$state = $state;
@ -36,7 +37,6 @@ class KubernetesConfigurationController {
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesEventService = KubernetesEventService;
this.KubernetesConfigurationTypes = KubernetesConfigurationTypes;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.KubernetesConfigMapService = KubernetesConfigMapService;
this.KubernetesSecretService = KubernetesSecretService;
@ -52,7 +52,7 @@ class KubernetesConfigurationController {
}
isSystemNamespace() {
return this.KubernetesNamespaceHelper.isSystemNamespace(this.configuration.Namespace);
return KubernetesNamespaceHelper.isSystemNamespace(this.configuration.Namespace);
}
isSystemConfig() {

View file

@ -5,6 +5,7 @@ import { KubernetesFormValidationReferences } from 'Kubernetes/models/applicatio
import { KubernetesIngressClass } from 'Kubernetes/ingress/models';
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
import { KubernetesIngressClassTypes } from 'Kubernetes/ingress/constants';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
class KubernetesConfigureController {
/* #region CONSTRUCTOR */
@ -18,7 +19,6 @@ class KubernetesConfigureController {
EndpointService,
EndpointProvider,
ModalService,
KubernetesNamespaceHelper,
KubernetesResourcePoolService,
KubernetesIngressService,
KubernetesMetricsService
@ -30,7 +30,6 @@ class KubernetesConfigureController {
this.EndpointService = EndpointService;
this.EndpointProvider = EndpointProvider;
this.ModalService = ModalService;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesIngressService = KubernetesIngressService;
this.KubernetesMetricsService = KubernetesMetricsService;
@ -147,8 +146,7 @@ class KubernetesConfigureController {
const allResourcePools = await this.KubernetesResourcePoolService.get();
const resourcePools = _.filter(
allResourcePools,
(resourcePool) =>
!this.KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && !this.KubernetesNamespaceHelper.isDefaultNamespace(resourcePool.Namespace.Name)
(resourcePool) => !KubernetesNamespaceHelper.isSystemNamespace(resourcePool.Namespace.Name) && !KubernetesNamespaceHelper.isDefaultNamespace(resourcePool.Namespace.Name)
);
ingressesToDel.forEach((ingress) => {

View file

@ -1,6 +1,7 @@
import angular from 'angular';
import _ from 'lodash-es';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
class KubernetesDashboardController {
/* @ngInject */
@ -13,7 +14,6 @@ class KubernetesDashboardController {
KubernetesApplicationService,
KubernetesConfigurationService,
KubernetesVolumeService,
KubernetesNamespaceHelper,
Authentication,
TagService
) {
@ -25,7 +25,6 @@ class KubernetesDashboardController {
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesConfigurationService = KubernetesConfigurationService;
this.KubernetesVolumeService = KubernetesVolumeService;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.Authentication = Authentication;
this.TagService = TagService;
@ -65,13 +64,8 @@ class KubernetesDashboardController {
: '-';
if (!isAdmin) {
this.pools = _.filter(pools, (pool) => {
return !this.KubernetesNamespaceHelper.isSystemNamespace(pool.Namespace.Name);
});
this.configurations = _.filter(configurations, (config) => {
return !KubernetesConfigurationHelper.isSystemToken(config);
});
this.pools = _.filter(pools, (pool) => !KubernetesNamespaceHelper.isSystemNamespace(pool.Namespace.Name));
this.configurations = _.filter(configurations, (config) => !KubernetesConfigurationHelper.isSystemToken(config));
} else {
this.pools = pools;
this.configurations = configurations;

View file

@ -15,9 +15,18 @@
<form class="form-horizontal" autocomplete="off" name="resourcePoolEditForm" style="padding: 20px; margin-top: 10px;">
<!-- name-input -->
<div class="form-group">
<label for="pool_name" class="col-sm-1 control-label text-left">Name</label>
<div class="col-sm-11">
<input type="text" class="form-control" name="pool_name" ng-model="ctrl.pool.Namespace.Name" disabled />
<div class="col-sm-12">
<table class="table">
<tbody>
<tr>
<td>Name</td>
<td>
{{ ctrl.pool.Namespace.Name }}
<span class="label label-info image-tag label-margins" ng-if="ctrl.isSystem">system</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- !name-input -->
@ -396,13 +405,14 @@
<!-- !summary -->
<!-- actions -->
<div ng-if="ctrl.isAdmin && ctrl.isEditable" class="col-sm-12 form-section-title">
<div ng-if="ctrl.isAdmin && !ctrl.isDefaultNamespace" class="col-sm-12 form-section-title">
Actions
</div>
<div ng-if="ctrl.isAdmin && ctrl.isEditable" class="form-group">
<div ng-if="ctrl.isAdmin && !ctrl.isDefaultNamespace" class="form-group">
<div class="col-sm-12">
<button
type="button"
ng-if="ctrl.isEditable"
class="btn btn-primary btn-sm"
ng-disabled="!resourcePoolEditForm.$valid || ctrl.isUpdateButtonDisabled()"
ng-click="ctrl.updateResourcePool()"
@ -411,6 +421,10 @@
<span ng-hide="ctrl.state.actionInProgress" data-cy="k8sNamespaceEdit-updateNamespaceButton">Update namespace</span>
<span ng-show="ctrl.state.actionInProgress">Update in progress...</span>
</button>
<button type="button" class="btn btn-primary btn-sm" ng-click="ctrl.markUnmarkAsSystem()" button-spinner="ctrl.state.actionInProgress">
<span ng-if="ctrl.isSystem">Unmark as system</span>
<span ng-if="!ctrl.isSystem">Mark as system</span>
</button>
</div>
</div>
<!-- !actions -->

View file

@ -15,6 +15,7 @@ import { KubernetesFormValidationReferences } from 'Kubernetes/models/applicatio
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
import { KubernetesIngressClassTypes } from 'Kubernetes/ingress/constants';
import KubernetesResourceQuotaConverter from 'Kubernetes/converters/resourceQuota';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
class KubernetesResourcePoolController {
/* #region CONSTRUCTOR */
@ -34,7 +35,6 @@ class KubernetesResourcePoolController {
KubernetesEventService,
KubernetesPodService,
KubernetesApplicationService,
KubernetesNamespaceHelper,
KubernetesIngressService,
KubernetesVolumeService
) {
@ -53,7 +53,6 @@ class KubernetesResourcePoolController {
KubernetesEventService,
KubernetesPodService,
KubernetesApplicationService,
KubernetesNamespaceHelper,
KubernetesIngressService,
KubernetesVolumeService,
});
@ -171,11 +170,11 @@ class KubernetesResourcePoolController {
}
/* #region UPDATE NAMESPACE */
async updateResourcePoolAsync() {
async updateResourcePoolAsync(oldFormValues, newFormValues) {
this.state.actionInProgress = true;
try {
this.checkDefaults();
await this.KubernetesResourcePoolService.patch(this.savedFormValues, this.formValues);
await this.KubernetesResourcePoolService.patch(oldFormValues, newFormValues);
this.Notifications.success('Namespace successfully updated', this.pool.Namespace.Name);
this.$state.reload();
} catch (err) {
@ -202,13 +201,45 @@ class KubernetesResourcePoolController {
${warnings.ingress ? messages.ingress : ''}<br/><br/>Do you wish to continue?`;
this.ModalService.confirmUpdate(displayedMessage, (confirmed) => {
if (confirmed) {
return this.$async(this.updateResourcePoolAsync);
return this.$async(this.updateResourcePoolAsync, this.savedFormValues, this.formValues);
}
});
} else {
return this.$async(this.updateResourcePoolAsync);
return this.$async(this.updateResourcePoolAsync, this.savedFormValues, this.formValues);
}
}
async confirmMarkUnmarkAsSystem() {
const message = this.isSystem
? 'Unmarking this namespace as system will allow non administrator users to manage it and the resources in contains depending on the access control settings. Are you sure?'
: 'Marking this namespace as a system namespace will prevent non administrator users from managing it and the resources it contains. Are you sure?';
return new Promise((resolve) => {
this.ModalService.confirmUpdate(message, resolve);
});
}
markUnmarkAsSystem() {
return this.$async(async () => {
try {
const namespaceName = this.$state.params.id;
this.state.actionInProgress = true;
const confirmed = await this.confirmMarkUnmarkAsSystem();
if (!confirmed) {
return;
}
await this.KubernetesResourcePoolService.toggleSystem(this.endpoint.Id, namespaceName, !this.isSystem);
this.Notifications.success('Namespace successfully updated', namespaceName);
this.$state.reload();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to create namespace');
} finally {
this.state.actionInProgress = false;
}
});
}
/* #endregion */
hasEventWarnings() {
@ -361,6 +392,7 @@ class KubernetesResourcePoolController {
this.formValues = new KubernetesResourcePoolFormValues(KubernetesResourceQuotaDefaults);
this.formValues.Name = this.pool.Namespace.Name;
this.formValues.EndpointId = this.endpoint.Id;
this.formValues.IsSystem = this.pool.Namespace.IsSystem;
_.forEach(nodes, (item) => {
this.state.sliderMaxMemory += filesizeParser(item.Memory);
@ -377,11 +409,9 @@ class KubernetesResourcePoolController {
this.state.resourceReservation.CPU = quota.CpuLimitUsed;
this.state.resourceReservation.Memory = KubernetesResourceReservationHelper.megaBytesValue(quota.MemoryLimitUsed);
}
this.isEditable = !this.KubernetesNamespaceHelper.isSystemNamespace(this.pool.Namespace.Name);
if (this.pool.Namespace.Name === 'default') {
this.isEditable = false;
}
this.isSystem = KubernetesNamespaceHelper.isSystemNamespace(this.pool.Namespace.Name);
this.isDefaultNamespace = KubernetesNamespaceHelper.isDefaultNamespace(this.pool.Namespace.Name);
this.isEditable = !this.isSystem && !this.isDefaultNamespace;
await this.getEvents();
await this.getApplications();

View file

@ -30,7 +30,7 @@
<td>Namespace</td>
<td>
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: ctrl.volume.ResourcePool.Namespace.Name })">{{ ctrl.volume.ResourcePool.Namespace.Name }}</a>
<span style="margin-left: 5px;" class="label label-info image-tag" ng-if="ctrl.isSystemNamespace(item)">system</span>
<span style="margin-left: 5px;" class="label label-info image-tag" ng-if="ctrl.isSystemNamespace()">system</span>
</td>
</tr>
<tr>

View file

@ -4,6 +4,7 @@ import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
import { KubernetesStorageClassAccessPolicies } from 'Kubernetes/models/storage-class/models';
import filesizeParser from 'filesize-parser';
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
class KubernetesVolumeController {
/* @ngInject */
@ -14,7 +15,6 @@ class KubernetesVolumeController {
LocalStorage,
KubernetesVolumeService,
KubernetesEventService,
KubernetesNamespaceHelper,
KubernetesApplicationService,
KubernetesPersistentVolumeClaimService,
ModalService,
@ -27,7 +27,6 @@ class KubernetesVolumeController {
this.KubernetesVolumeService = KubernetesVolumeService;
this.KubernetesEventService = KubernetesEventService;
this.KubernetesNamespaceHelper = KubernetesNamespaceHelper;
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesPersistentVolumeClaimService = KubernetesPersistentVolumeClaimService;
this.ModalService = ModalService;
@ -55,7 +54,7 @@ class KubernetesVolumeController {
}
isSystemNamespace() {
return this.KubernetesNamespaceHelper.isSystemNamespace(this.volume.ResourcePool.Namespace.Name);
return KubernetesNamespaceHelper.isSystemNamespace(this.volume.ResourcePool.Namespace.Name);
}
isUsed() {