portainer/app/kubernetes/views/resource-pools/create/createResourcePoolControlle...

280 lines
9.5 KiB
JavaScript

import _ from 'lodash-es';
import filesizeParser from 'filesize-parser';
import { KubernetesResourceQuotaDefaults } from 'Kubernetes/models/resource-quota/models';
import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper';
import {
KubernetesResourcePoolFormValues,
KubernetesResourcePoolIngressClassAnnotationFormValue,
KubernetesResourcePoolIngressClassHostFormValue,
KubernetesResourcePoolNginxRewriteAnnotationFormValue,
KubernetesResourcePoolNginxUseregexAnnotationFormValue,
KubernetesResourcePoolTraefikRewriteAnnotationFormValue,
} from 'Kubernetes/models/resource-pool/formValues';
import { KubernetesIngressConverter } from 'Kubernetes/ingress/converter';
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
import { KubernetesFormValidationReferences } from 'Kubernetes/models/application/formValues';
import { KubernetesIngressClassTypes } from 'Kubernetes/ingress/constants';
import { FeatureId } from '@/portainer/feature-flags/enums';
class KubernetesCreateResourcePoolController {
/* #region CONSTRUCTOR */
/* @ngInject */
constructor($async, $state, $scope, Notifications, KubernetesNodeService, KubernetesResourcePoolService, KubernetesIngressService, Authentication, EndpointService) {
Object.assign(this, {
$async,
$state,
$scope,
Notifications,
KubernetesNodeService,
KubernetesResourcePoolService,
KubernetesIngressService,
Authentication,
EndpointService,
});
this.IngressClassTypes = KubernetesIngressClassTypes;
this.LBQuotaFeatureId = FeatureId.K8S_RESOURCE_POOL_LB_QUOTA;
this.onToggleStorageQuota = this.onToggleStorageQuota.bind(this);
this.onToggleLoadBalancerQuota = this.onToggleLoadBalancerQuota.bind(this);
this.onToggleResourceQuota = this.onToggleResourceQuota.bind(this);
this.onRegistriesChange = this.onRegistriesChange.bind(this);
}
/* #endregion */
onToggleStorageQuota(storageClassName, enabled) {
this.$scope.$evalAsync(() => {
this.formValues.StorageClasses = this.formValues.StorageClasses.map((sClass) => (sClass.Name !== storageClassName ? sClass : { ...sClass, Selected: enabled }));
});
}
onToggleLoadBalancerQuota(enabled) {
this.$scope.$evalAsync(() => {
this.formValues.UseLoadBalancersQuota = enabled;
});
}
onToggleResourceQuota(enabled) {
this.$scope.$evalAsync(() => {
this.formValues.HasQuota = enabled;
});
}
onChangeIngressHostname() {
const state = this.state.duplicates.ingressHosts;
const hosts = _.flatMap(this.formValues.IngressClasses, 'Hosts');
const hostnames = _.compact(hosts.map((h) => h.Host));
const hostnamesWithoutRemoved = _.filter(hostnames, (h) => !h.NeedsDeletion);
const allHosts = _.flatMap(this.allIngresses, 'Hosts');
const formDuplicates = KubernetesFormValidationHelper.getDuplicates(hostnamesWithoutRemoved);
_.forEach(hostnames, (host, idx) => {
if (host !== undefined && _.includes(allHosts, host)) {
formDuplicates[idx] = host;
}
});
const duplicates = {};
let count = 0;
_.forEach(this.formValues.IngressClasses, (ic) => {
duplicates[ic.IngressClass.Name] = {};
_.forEach(ic.Hosts, (hostFV, hostIdx) => {
if (hostFV.Host === formDuplicates[count]) {
duplicates[ic.IngressClass.Name][hostIdx] = hostFV.Host;
}
count++;
});
});
state.refs = duplicates;
state.hasRefs = false;
_.forIn(duplicates, (value) => {
if (Object.keys(value).length > 0) {
state.hasRefs = true;
}
});
}
onRegistriesChange(registries) {
return this.$scope.$evalAsync(() => {
this.formValues.Registries = registries;
});
}
addHostname(ingressClass) {
ingressClass.Hosts.push(new KubernetesResourcePoolIngressClassHostFormValue());
}
removeHostname(ingressClass, index) {
ingressClass.Hosts.splice(index, 1);
this.onChangeIngressHostname();
}
/* #region ANNOTATIONS MANAGEMENT */
addAnnotation(ingressClass) {
ingressClass.Annotations.push(new KubernetesResourcePoolIngressClassAnnotationFormValue());
}
addRewriteAnnotation(ingressClass) {
if (ingressClass.IngressClass.Type === this.IngressClassTypes.NGINX) {
ingressClass.Annotations.push(new KubernetesResourcePoolNginxRewriteAnnotationFormValue());
}
if (ingressClass.IngressClass.Type === this.IngressClassTypes.TRAEFIK) {
ingressClass.Annotations.push(new KubernetesResourcePoolTraefikRewriteAnnotationFormValue());
}
}
addUseregexAnnotation(ingressClass) {
ingressClass.Annotations.push(new KubernetesResourcePoolNginxUseregexAnnotationFormValue());
}
removeAnnotation(ingressClass, index) {
ingressClass.Annotations.splice(index, 1);
}
/* #endregion */
isCreateButtonDisabled() {
return (
this.state.actionInProgress ||
(this.formValues.HasQuota && !this.isQuotaValid()) ||
this.state.isAlreadyExist ||
this.state.hasPrefixKube ||
this.state.duplicates.ingressHosts.hasRefs
);
}
onChangeName() {
this.state.isAlreadyExist = _.find(this.resourcePools, (resourcePool) => resourcePool.Namespace.Name === this.formValues.Name) !== undefined;
this.state.hasPrefixKube = /^kube-/.test(this.formValues.Name);
}
isQuotaValid() {
if (
this.state.sliderMaxCpu < this.formValues.CpuLimit ||
this.state.sliderMaxMemory < this.formValues.MemoryLimit ||
(this.formValues.CpuLimit === 0 && this.formValues.MemoryLimit === 0)
) {
return false;
}
return true;
}
checkDefaults() {
if (this.formValues.CpuLimit < this.defaults.CpuLimit) {
this.formValues.CpuLimit = this.defaults.CpuLimit;
}
if (this.formValues.MemoryLimit < KubernetesResourceReservationHelper.megaBytesValue(this.defaults.MemoryLimit)) {
this.formValues.MemoryLimit = KubernetesResourceReservationHelper.megaBytesValue(this.defaults.MemoryLimit);
}
}
/* #region CREATE NAMESPACE */
createResourcePool() {
return this.$async(async () => {
this.state.actionInProgress = true;
try {
this.checkDefaults();
this.formValues.Owner = this.Authentication.getUserDetails().username;
await this.KubernetesResourcePoolService.create(this.formValues);
this.Notifications.success('Namespace successfully created', this.formValues.Name);
this.$state.go('kubernetes.resourcePools');
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to create namespace');
} finally {
this.state.actionInProgress = false;
}
});
}
/* #endregion */
/* #region GET INGRESSES */
getIngresses() {
return this.$async(async () => {
try {
this.allIngresses = await this.KubernetesIngressService.get();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve ingresses.');
}
});
}
/* #endregion */
/* #region GET NAMESPACES */
getResourcePools() {
return this.$async(async () => {
try {
this.resourcePools = await this.KubernetesResourcePoolService.get();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve namespaces');
}
});
}
/* #endregion */
/* #region GET REGISTRIES */
getRegistries() {
return this.$async(async () => {
try {
this.registries = await this.EndpointService.registries(this.endpoint.Id);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve registries');
}
});
}
/* #endregion */
/* #region ON INIT */
$onInit() {
return this.$async(async () => {
try {
const endpoint = this.endpoint;
this.defaults = KubernetesResourceQuotaDefaults;
this.formValues = new KubernetesResourcePoolFormValues(this.defaults);
this.formValues.EndpointId = this.endpoint.Id;
this.formValues.HasQuota = true;
this.state = {
actionInProgress: false,
sliderMaxMemory: 0,
sliderMaxCpu: 0,
viewReady: false,
isAlreadyExist: false,
hasPrefixKube: false,
canUseIngress: endpoint.Kubernetes.Configuration.IngressClasses.length,
duplicates: {
ingressHosts: new KubernetesFormValidationReferences(),
},
isAdmin: this.Authentication.isAdmin(),
};
const nodes = await this.KubernetesNodeService.get();
_.forEach(nodes, (item) => {
this.state.sliderMaxMemory += filesizeParser(item.Memory);
this.state.sliderMaxCpu += item.CPU;
});
this.state.sliderMaxMemory = KubernetesResourceReservationHelper.megaBytesValue(this.state.sliderMaxMemory);
await this.getResourcePools();
if (this.state.canUseIngress) {
await this.getIngresses();
const ingressClasses = endpoint.Kubernetes.Configuration.IngressClasses;
this.formValues.IngressClasses = KubernetesIngressConverter.ingressClassesToFormValues(ingressClasses);
}
_.forEach(this.formValues.IngressClasses, (ic) => {
if (ic.Hosts.length === 0) {
ic.Hosts.push(new KubernetesResourcePoolIngressClassHostFormValue());
}
});
await this.getRegistries();
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load view data');
} finally {
this.state.viewReady = true;
}
});
}
/* #endregion */
}
export default KubernetesCreateResourcePoolController;