From abf517de2826334271507ebeedf92dbbe13cf344 Mon Sep 17 00:00:00 2001 From: Ali <83188384+testA113@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:42:36 +1300 Subject: [PATCH] refactor(app): migrate app summary section [EE-6239] (#10910) --- .../applicationsDatatable.html | 14 +- .../applicationsDatatableController.js | 9 +- .../nodeApplicationsDatatableController.js | 2 +- app/kubernetes/converters/application.js | 55 +- .../converters/persistentVolumeClaim.js | 2 +- app/kubernetes/converters/service.js | 28 +- app/kubernetes/filters/applicationFilters.js | 40 +- app/kubernetes/helpers/application/index.js | 30 +- app/kubernetes/helpers/volumeHelper.js | 6 +- .../horizontal-pod-auto-scaler/converter.js | 84 +-- .../horizontal-pod-auto-scaler/helper.js | 21 +- app/kubernetes/ingress/converter.js | 31 -- .../models/application/formValues.js | 18 +- .../models/application/models/appConstants.ts | 41 ++ .../models/application/models/constants.js | 32 -- app/kubernetes/react/components/index.ts | 41 +- app/kubernetes/services/applicationService.js | 26 +- .../applications/applicationsController.js | 4 +- .../create/createApplication.html | 28 +- .../create/createApplicationController.js | 139 ++--- .../summary/resources/applicationResources.js | 34 +- .../AdvancedMode.tsx | 6 +- .../EnvironmentVariablesFieldset.tsx | 10 +- .../EnvironmentVariablesPanel.tsx | 8 +- .../SimpleMode.tsx | 10 +- .../EnvironmentVariablesFieldset/index.ts | 2 +- .../EnvironmentVariablesFieldset/types.ts | 2 +- .../CreateView/EnvVarsTab/EnvVarsTab.tsx | 15 +- .../containers/CreateView/EnvVarsTab/index.ts | 1 - .../CreateView/EnvVarsTab/toRequest.ts | 5 +- .../containers/CreateView/EnvVarsTab/types.ts | 1 - .../containers/CreateView/useInitialValues.ts | 9 +- .../AppDeploymentTypeFormSection.tsx | 6 +- .../DataAccessPolicyFormSection.tsx | 23 +- .../application-services/KubeServicesForm.tsx | 31 +- .../cluster-ip/ClusterIpServiceForm.tsx | 10 +- .../cluster-ip/ClusterIpServicesForm.tsx | 10 +- .../components/ContainerPortInput.tsx | 2 +- .../components/ServicePortInput.tsx | 2 +- .../components/ServiceTabs.tsx | 8 +- .../kubeServicesValidation.ts | 31 +- .../load-balancer/LoadBalancerServiceForm.tsx | 17 +- .../LoadBalancerServicesForm.tsx | 10 +- .../node-port/NodePortServiceForm.tsx | 12 +- .../node-port/NodePortServicesForm.tsx | 9 +- .../CreateView/application-services/types.ts | 13 +- .../CreateView/application-services/utils.ts | 83 ++- .../CreateView/deploymentOptions.tsx | 10 +- .../ApplicationSummarySection.tsx | 73 +++ .../ApplicationSummarySection/index.ts | 1 + .../ApplicationSummarySection/types.ts | 21 + .../ApplicationSummarySection/utils.test.ts | 517 ++++++++++++++++++ .../ApplicationSummarySection/utils.ts | 362 ++++++++++++ .../PersistedFolderItem.tsx | 4 +- .../PersistedFoldersFormSection.tsx | 38 +- .../PlacementFormSection.tsx | 6 +- .../kubernetes/applications/constants.ts | 3 +- app/react/kubernetes/applications/types.ts | 41 +- .../CreateIngressView/CreateIngressView.tsx | 4 +- .../ingresses/CreateIngressView/utils.ts | 8 - app/react/kubernetes/utils.ts | 13 + 61 files changed, 1461 insertions(+), 661 deletions(-) create mode 100644 app/kubernetes/models/application/models/appConstants.ts delete mode 100644 app/react/docker/containers/CreateView/EnvVarsTab/types.ts create mode 100644 app/react/kubernetes/applications/components/ApplicationSummarySection/ApplicationSummarySection.tsx create mode 100644 app/react/kubernetes/applications/components/ApplicationSummarySection/index.ts create mode 100644 app/react/kubernetes/applications/components/ApplicationSummarySection/types.ts create mode 100644 app/react/kubernetes/applications/components/ApplicationSummarySection/utils.test.ts create mode 100644 app/react/kubernetes/applications/components/ApplicationSummarySection/utils.ts diff --git a/app/kubernetes/components/datatables/applications-datatable/applicationsDatatable.html b/app/kubernetes/components/datatables/applications-datatable/applicationsDatatable.html index 6bbe34867..b4ce05c44 100644 --- a/app/kubernetes/components/datatables/applications-datatable/applicationsDatatable.html +++ b/app/kubernetes/components/datatables/applications-datatable/applicationsDatatable.html @@ -219,7 +219,7 @@
@@ -282,7 +282,7 @@ {{ item.Name }} @@ -297,17 +297,17 @@ {{ item.Image | truncate: 64 }} + {{ item.Containers.length - 1 }} - {{ item.ApplicationType | kubernetesApplicationTypeText }} - + {{ item.ApplicationType }} + - Replicated - Global + Replicated + Global {{ item.RunningPodsCount }} / {{ item.TotalPodsCount }} {{ item.Status }} - + {{ item.Pods[0].Status }} diff --git a/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js b/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js index d80b951fe..527a2d6e2 100644 --- a/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js +++ b/app/kubernetes/components/datatables/applications-datatable/applicationsDatatableController.js @@ -1,8 +1,8 @@ import _ from 'lodash-es'; -import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models'; import KubernetesApplicationHelper from 'Kubernetes/helpers/application'; import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper'; import { KubernetesConfigurationKinds } from 'Kubernetes/models/configuration/models'; +import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; angular.module('portainer.kubernetes').controller('KubernetesApplicationsDatatableController', [ '$scope', @@ -33,13 +33,6 @@ angular.module('portainer.kubernetes').controller('KubernetesApplicationsDatatab }, }; - this.applicationTypeEnumToParamMap = { - [KubernetesApplicationTypes.DEPLOYMENT]: 'Deployment', - [KubernetesApplicationTypes.DAEMONSET]: 'DaemonSet', - [KubernetesApplicationTypes.STATEFULSET]: 'StatefulSet', - [KubernetesApplicationTypes.POD]: 'Pod', - }; - this.expandAll = function () { this.state.expandAll = !this.state.expandAll; this.state.filteredDataSet.forEach((item) => this.expandItem(item, this.state.expandAll)); diff --git a/app/kubernetes/components/datatables/node-applications-datatable/nodeApplicationsDatatableController.js b/app/kubernetes/components/datatables/node-applications-datatable/nodeApplicationsDatatableController.js index 447dcc013..3c9d19e24 100644 --- a/app/kubernetes/components/datatables/node-applications-datatable/nodeApplicationsDatatableController.js +++ b/app/kubernetes/components/datatables/node-applications-datatable/nodeApplicationsDatatableController.js @@ -1,4 +1,4 @@ -import { KubernetesApplicationDeploymentTypes } from 'Kubernetes/models/application/models'; +import { KubernetesApplicationDeploymentTypes } from 'Kubernetes/models/application/models/appConstants'; import KubernetesApplicationHelper from 'Kubernetes/helpers/application'; import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper'; diff --git a/app/kubernetes/converters/application.js b/app/kubernetes/converters/application.js index d3161b410..524c92511 100644 --- a/app/kubernetes/converters/application.js +++ b/app/kubernetes/converters/application.js @@ -1,15 +1,12 @@ import _ from 'lodash-es'; import filesizeParser from 'filesize-parser'; +import { KubernetesApplicationDataAccessPolicies, KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; import { KubernetesApplication, KubernetesApplicationConfigurationVolume, - KubernetesApplicationDataAccessPolicies, - KubernetesApplicationDeploymentTypes, KubernetesApplicationPersistedFolder, KubernetesApplicationPort, - KubernetesApplicationPublishingTypes, - KubernetesApplicationTypes, KubernetesPortainerApplicationNameLabel, KubernetesPortainerApplicationNote, KubernetesPortainerApplicationOwnerLabel, @@ -241,16 +238,16 @@ class KubernetesApplicationConverter { static apiPodToApplication(data, pods, service, ingresses) { const res = new KubernetesApplication(); KubernetesApplicationConverter.applicationCommon(res, data, pods, service, ingresses); - res.ApplicationType = KubernetesApplicationTypes.POD; + res.ApplicationType = KubernetesApplicationTypes.Pod; return res; } static apiDeploymentToApplication(data, pods, service, ingresses) { const res = new KubernetesApplication(); KubernetesApplicationConverter.applicationCommon(res, data, pods, service, ingresses); - res.ApplicationType = KubernetesApplicationTypes.DEPLOYMENT; - res.DeploymentType = KubernetesApplicationDeploymentTypes.REPLICATED; - res.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.SHARED; + res.ApplicationType = KubernetesApplicationTypes.Deployment; + res.DeploymentType = KubernetesApplicationDeploymentTypes.Replicated; + res.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.Shared; res.RunningPodsCount = data.status.availableReplicas || data.status.replicas - data.status.unavailableReplicas || 0; res.TotalPodsCount = data.spec.replicas; return res; @@ -259,9 +256,9 @@ class KubernetesApplicationConverter { static apiDaemonSetToApplication(data, pods, service, ingresses) { const res = new KubernetesApplication(); KubernetesApplicationConverter.applicationCommon(res, data, pods, service, ingresses); - res.ApplicationType = KubernetesApplicationTypes.DAEMONSET; - res.DeploymentType = KubernetesApplicationDeploymentTypes.GLOBAL; - res.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.SHARED; + res.ApplicationType = KubernetesApplicationTypes.DaemonSet; + res.DeploymentType = KubernetesApplicationDeploymentTypes.Global; + res.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.Shared; res.RunningPodsCount = data.status.numberAvailable || data.status.desiredNumberScheduled - data.status.numberUnavailable || 0; res.TotalPodsCount = data.status.desiredNumberScheduled; return res; @@ -270,9 +267,9 @@ class KubernetesApplicationConverter { static apiStatefulSetToapplication(data, pods, service, ingresses) { const res = new KubernetesApplication(); KubernetesApplicationConverter.applicationCommon(res, data, pods, service, ingresses); - res.ApplicationType = KubernetesApplicationTypes.STATEFULSET; - res.DeploymentType = KubernetesApplicationDeploymentTypes.REPLICATED; - res.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.ISOLATED; + res.ApplicationType = KubernetesApplicationTypes.StatefulSet; + res.DeploymentType = KubernetesApplicationDeploymentTypes.Replicated; + res.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.Isolated; res.RunningPodsCount = data.status.readyReplicas || 0; res.TotalPodsCount = data.spec.replicas; res.HeadlessServiceName = data.spec.serviceName; @@ -313,16 +310,7 @@ class KubernetesApplicationConverter { res.PublishedPorts = KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(app.ServiceType, app.PublishedPorts, ingresses); res.Containers = app.Containers; - const isIngress = _.filter(res.PublishedPorts, (p) => p.IngressName).length; - if (app.ServiceType === KubernetesServiceTypes.LOAD_BALANCER) { - res.PublishingType = KubernetesApplicationPublishingTypes.LOAD_BALANCER; - } else if (app.ServiceType === KubernetesServiceTypes.NODE_PORT) { - res.PublishingType = KubernetesApplicationPublishingTypes.NODE_PORT; - } else if (app.ServiceType === KubernetesServiceTypes.CLUSTER_IP && isIngress) { - res.PublishingType = KubernetesApplicationPublishingTypes.INGRESS; - } else { - res.PublishingType = KubernetesApplicationPublishingTypes.CLUSTER_IP; - } + res.PublishingType = app.ServiceType; if (app.Pods && app.Pods.length) { KubernetesApplicationHelper.generatePlacementsFormValuesFromAffinity(res, app.Pods[0].Affinity); @@ -338,20 +326,20 @@ class KubernetesApplicationConverter { const rwx = KubernetesApplicationHelper.hasRWX(claims); const deployment = - (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.REPLICATED && - (claims.length === 0 || (claims.length > 0 && formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.SHARED))) || - formValues.ApplicationType === KubernetesApplicationTypes.DEPLOYMENT; + (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.Replicated && + (claims.length === 0 || (claims.length > 0 && formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.Shared))) || + formValues.ApplicationType === KubernetesApplicationTypes.Deployment; const statefulSet = - (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.REPLICATED && + (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.Replicated && claims.length > 0 && - formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.ISOLATED) || - formValues.ApplicationType === KubernetesApplicationTypes.STATEFULSET; + formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.Isolated) || + formValues.ApplicationType === KubernetesApplicationTypes.StatefulSet; const daemonSet = - (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.GLOBAL && - (claims.length === 0 || (claims.length > 0 && formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.SHARED && rwx))) || - formValues.ApplicationType === KubernetesApplicationTypes.DAEMONSET; + (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.Global && + (claims.length === 0 || (claims.length > 0 && formValues.DataAccessPolicy === KubernetesApplicationDataAccessPolicies.Shared && rwx))) || + formValues.ApplicationType === KubernetesApplicationTypes.DaemonSet; let app; if (deployment) { @@ -363,6 +351,7 @@ class KubernetesApplicationConverter { } else { throw new PortainerError('Unable to determine which association to use to convert form'); } + app.ApplicationType = formValues.ApplicationType; let headlessService; if (statefulSet) { diff --git a/app/kubernetes/converters/persistentVolumeClaim.js b/app/kubernetes/converters/persistentVolumeClaim.js index 363c7365e..c80563682 100644 --- a/app/kubernetes/converters/persistentVolumeClaim.js +++ b/app/kubernetes/converters/persistentVolumeClaim.js @@ -71,7 +71,7 @@ class KubernetesPersistentVolumeClaimConverter { res.metadata.namespace = pvc.Namespace; res.spec.resources.requests.storage = pvc.Storage; res.spec.storageClassName = pvc.storageClass ? pvc.storageClass.Name : ''; - const accessModes = pvc.StorageClass && pvc.StorageClass.AccessModes ? pvc.StorageClass.AccessModes.map((accessMode) => storageClassToPVCAccessModes[accessMode]) : []; + const accessModes = pvc.storageClass && pvc.storageClass.AccessModes ? pvc.storageClass.AccessModes.map((accessMode) => storageClassToPVCAccessModes[accessMode]) : []; res.spec.accessModes = accessModes; res.metadata.labels.app = pvc.ApplicationName; res.metadata.labels[KubernetesPortainerApplicationOwnerLabel] = pvc.ApplicationOwner; diff --git a/app/kubernetes/converters/service.js b/app/kubernetes/converters/service.js index 420c6df26..bda4b6a0e 100644 --- a/app/kubernetes/converters/service.js +++ b/app/kubernetes/converters/service.js @@ -3,7 +3,6 @@ import * as JsonPatch from 'fast-json-patch'; import { KubernetesServiceCreatePayload } from 'Kubernetes/models/service/payloads'; import { - KubernetesApplicationPublishingTypes, KubernetesPortainerApplicationNameLabel, KubernetesPortainerApplicationOwnerLabel, KubernetesPortainerApplicationStackNameLabel, @@ -42,11 +41,7 @@ class KubernetesServiceConverter { res.StackName = formValues.StackName ? formValues.StackName : formValues.Name; res.ApplicationOwner = formValues.ApplicationOwner; res.ApplicationName = formValues.Name; - if (formValues.PublishingType === KubernetesApplicationPublishingTypes.NODE_PORT) { - res.Type = KubernetesServiceTypes.NODE_PORT; - } else if (formValues.PublishingType === KubernetesApplicationPublishingTypes.LOAD_BALANCER) { - res.Type = KubernetesServiceTypes.LOAD_BALANCER; - } + res.Type = formValues.PublishingType; const ports = _.map(formValues.PublishedPorts, (item) => _publishedPortToServicePort(formValues, item, res.Type)); res.Ports = _.uniqBy(_.without(ports, undefined), (p) => p.targetPort + p.protocol); return res; @@ -61,13 +56,7 @@ class KubernetesServiceConverter { res.StackName = formValues.StackName ? formValues.StackName : formValues.Name; res.ApplicationOwner = formValues.ApplicationOwner; res.ApplicationName = formValues.Name; - if (service.Type === KubernetesApplicationPublishingTypes.NODE_PORT) { - res.Type = KubernetesServiceTypes.NODE_PORT; - } else if (service.Type === KubernetesApplicationPublishingTypes.LOAD_BALANCER) { - res.Type = KubernetesServiceTypes.LOAD_BALANCER; - } else if (service.Type === KubernetesApplicationPublishingTypes.CLUSTER_IP) { - res.Type = KubernetesServiceTypes.CLUSTER_IP; - } + res.Type = service.Type; res.Ingress = service.Ingress; if (service.Selector !== undefined) { @@ -120,18 +109,7 @@ class KubernetesServiceConverter { payload.metadata.labels[KubernetesPortainerApplicationNameLabel] = service.ApplicationName; payload.metadata.labels[KubernetesPortainerApplicationOwnerLabel] = service.ApplicationOwner; - const ports = []; - service.Ports.forEach((port) => { - const p = {}; - p.name = port.name; - p.port = port.port; - p.nodePort = port.nodePort; - p.protocol = port.protocol; - p.targetPort = port.targetPort; - ports.push(p); - }); - payload.spec.ports = ports; - + payload.spec.ports = service.Ports; payload.spec.selector = service.Selector; if (service.Headless) { payload.spec.clusterIP = KubernetesServiceHeadlessClusterIP; diff --git a/app/kubernetes/filters/applicationFilters.js b/app/kubernetes/filters/applicationFilters.js index 8b4fe62c0..4b1c97649 100644 --- a/app/kubernetes/filters/applicationFilters.js +++ b/app/kubernetes/filters/applicationFilters.js @@ -1,29 +1,8 @@ import _ from 'lodash-es'; -import { KubernetesApplicationDataAccessPolicies } from 'Kubernetes/models/application/models'; -import { KubernetesApplicationTypes, KubernetesApplicationTypeStrings } from 'Kubernetes/models/application/models'; import { nodeAffinityValues } from './application'; angular .module('portainer.kubernetes') - .filter('kubernetesApplicationTypeText', function () { - 'use strict'; - return function (type) { - switch (type) { - case KubernetesApplicationTypes.DEPLOYMENT: - return KubernetesApplicationTypeStrings.DEPLOYMENT; - case KubernetesApplicationTypes.DAEMONSET: - return KubernetesApplicationTypeStrings.DAEMONSET; - case KubernetesApplicationTypes.STATEFULSET: - return KubernetesApplicationTypeStrings.STATEFULSET; - case KubernetesApplicationTypes.POD: - return KubernetesApplicationTypeStrings.POD; - case KubernetesApplicationTypes.HELM: - return KubernetesApplicationTypeStrings.HELM; - default: - return '-'; - } - }; - }) .filter('kubernetesApplicationCPUValue', function () { 'use strict'; return function (value) { @@ -34,31 +13,20 @@ angular 'use strict'; return function (value) { switch (value) { - case KubernetesApplicationDataAccessPolicies.ISOLATED: + case 'Isolated': return 'boxes'; - case KubernetesApplicationDataAccessPolicies.SHARED: + case 'Shared': return 'box'; } }; }) - .filter('kubernetesApplicationDataAccessPolicyText', function () { - 'use strict'; - return function (value) { - switch (value) { - case KubernetesApplicationDataAccessPolicies.ISOLATED: - return 'Isolated'; - case KubernetesApplicationDataAccessPolicies.SHARED: - return 'Shared'; - } - }; - }) .filter('kubernetesApplicationDataAccessPolicyTooltip', function () { 'use strict'; return function (value) { switch (value) { - case KubernetesApplicationDataAccessPolicies.ISOLATED: + case 'Isolated': return 'All the instances of this application are using their own data.'; - case KubernetesApplicationDataAccessPolicies.SHARED: + case 'Shared': return 'All the instances of this application are sharing the same data.'; } }; diff --git a/app/kubernetes/helpers/application/index.js b/app/kubernetes/helpers/application/index.js index 115f5208f..9d21e469e 100644 --- a/app/kubernetes/helpers/application/index.js +++ b/app/kubernetes/helpers/application/index.js @@ -22,7 +22,8 @@ import { KubernetesApplicationVolumeSecretPayload, } from 'Kubernetes/models/application/payloads'; import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper'; -import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes, HelmApplication } from 'Kubernetes/models/application/models'; +import { HelmApplication } from 'Kubernetes/models/application/models'; +import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; import { KubernetesPodAffinity, KubernetesPodNodeAffinityNodeSelectorRequirementOperators } from 'Kubernetes/pod/models'; import { KubernetesNodeSelectorRequirementPayload, @@ -287,13 +288,6 @@ class KubernetesApplicationHelper { svc.ApplicationOwner = app.ApplicationOwner; svc.ApplicationName = app.ApplicationName; svc.Type = service.spec.type; - if (service.spec.type === KubernetesServiceTypes.CLUSTER_IP) { - svc.Type = 1; - } else if (service.spec.type === KubernetesServiceTypes.NODE_PORT) { - svc.Type = 2; - } else if (service.spec.type === KubernetesServiceTypes.LOAD_BALANCER) { - svc.Type = 3; - } let ports = []; service.spec.ports.forEach(function (port) { @@ -373,15 +367,15 @@ class KubernetesApplicationHelper { static generateAutoScalerFormValueFromHorizontalPodAutoScaler(autoScaler, replicasCount) { const res = new KubernetesApplicationAutoScalerFormValue(); if (autoScaler) { - res.IsUsed = true; - res.MinReplicas = autoScaler.MinReplicas; - res.MaxReplicas = autoScaler.MaxReplicas; - res.TargetCPUUtilization = autoScaler.TargetCPUUtilization; - res.ApiVersion = autoScaler.ApiVersion; + res.isUsed = true; + res.minReplicas = autoScaler.MinReplicas; + res.maxReplicas = autoScaler.MaxReplicas; + res.targetCpuUtilizationPercentage = autoScaler.TargetCPUUtilization; + res.apiVersion = autoScaler.ApiVersion; } else { - res.ApiVersion = 'apps/v1'; - res.MinReplicas = replicasCount; - res.MaxReplicas = replicasCount; + res.apiVersion = 'apps/v1'; + res.minReplicas = replicasCount; + res.maxReplicas = replicasCount; } return res; } @@ -461,7 +455,7 @@ class KubernetesApplicationHelper { } static generateAffinityFromPlacements(app, formValues) { - if (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.REPLICATED) { + if (formValues.DeploymentType === KubernetesApplicationDeploymentTypes.Replicated) { const placements = formValues.Placements; const res = new KubernetesPodNodeAffinityPayload(); let expressions = _.map(placements, (p) => { @@ -545,7 +539,7 @@ class KubernetesApplicationHelper { const helmAppsList = helmAppsEntriesList.map(([helmInstance, applications]) => { const helmApp = new HelmApplication(); helmApp.Name = helmInstance; - helmApp.ApplicationType = KubernetesApplicationTypes.HELM; + helmApp.ApplicationType = KubernetesApplicationTypes.Helm; helmApp.ApplicationOwner = applications[0].ApplicationOwner; helmApp.KubernetesApplications = applications; diff --git a/app/kubernetes/helpers/volumeHelper.js b/app/kubernetes/helpers/volumeHelper.js index c357ffb14..0b158d8f5 100644 --- a/app/kubernetes/helpers/volumeHelper.js +++ b/app/kubernetes/helpers/volumeHelper.js @@ -1,11 +1,11 @@ import _ from 'lodash-es'; import uuidv4 from 'uuid/v4'; -import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models'; +import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; class KubernetesVolumeHelper { // TODO: review // the following condition - // && (app.ApplicationType === KubernetesApplicationTypes.STATEFULSET ? _.includes(volume.PersistentVolumeClaim.Name, app.Name) : true); + // && (app.ApplicationType === KubernetesApplicationTypes.StatefulSet ? _.includes(volume.PersistentVolumeClaim.Name, app.Name) : true); // is made to enforce finding the good SFS when multiple SFS in the same namespace // are referencing an internal PVC using the same internal name // (PVC are not exposed to other apps so they can have the same name in differents SFS) @@ -16,7 +16,7 @@ class KubernetesVolumeHelper { return ( volume.ResourcePool.Namespace.Name === app.ResourcePool && matchingNames.length && - (app.ApplicationType === KubernetesApplicationTypes.STATEFULSET ? _.includes(volume.PersistentVolumeClaim.Name, app.Name) : true) + (app.ApplicationType === KubernetesApplicationTypes.StatefulSet ? _.includes(volume.PersistentVolumeClaim.Name, app.Name) : true) ); }); } diff --git a/app/kubernetes/horizontal-pod-auto-scaler/converter.js b/app/kubernetes/horizontal-pod-auto-scaler/converter.js index 5ccfdf9c9..12841b0bf 100644 --- a/app/kubernetes/horizontal-pod-auto-scaler/converter.js +++ b/app/kubernetes/horizontal-pod-auto-scaler/converter.js @@ -30,7 +30,7 @@ export class KubernetesHorizontalPodAutoScalerConverter { payload.metadata.name = data.TargetEntity.Name; payload.spec.minReplicas = data.MinReplicas; payload.spec.maxReplicas = data.MaxReplicas; - payload.spec.targetCPUUtilizationPercentage = data.TargetCPUUtilization; + payload.spec.targetCPUUtilizationPercentage = data.targetCpuUtilizationPercentage; payload.spec.scaleTargetRef.apiVersion = data.TargetEntity.ApiVersion; payload.spec.scaleTargetRef.kind = data.TargetEntity.Kind; payload.spec.scaleTargetRef.name = data.TargetEntity.Name; @@ -48,86 +48,12 @@ export class KubernetesHorizontalPodAutoScalerConverter { const res = new KubernetesHorizontalPodAutoScaler(); res.Name = formValues.Name; res.Namespace = formValues.ResourcePool.Namespace.Name; - res.MinReplicas = formValues.AutoScaler.MinReplicas; - res.MaxReplicas = formValues.AutoScaler.MaxReplicas; - res.TargetCPUUtilization = formValues.AutoScaler.TargetCPUUtilization; + res.MinReplicas = formValues.AutoScaler.minReplicas; + res.MaxReplicas = formValues.AutoScaler.maxReplicas; + res.TargetCPUUtilization = formValues.AutoScaler.targetCpuUtilizationPercentage; res.TargetEntity.Name = formValues.Name; res.TargetEntity.Kind = kind; - res.TargetEntity.ApiVersion = formValues.AutoScaler.ApiVersion; + res.TargetEntity.ApiVersion = formValues.AutoScaler.apiVersion; return res; } - - /** - * Convertion functions to use with v2beta2 model - */ - - // static apiToModel(data, yaml) { - // const res = new KubernetesHorizontalPodAutoScaler(); - // res.Id = data.metadata.uid; - // res.Namespace = data.metadata.namespace; - // res.Name = data.metadata.name; - // res.MinReplicas = data.spec.minReplicas; - // res.MaxReplicas = data.spec.maxReplicas; - // res.TargetCPUUtilization = data.spec.targetCPUUtilization; - - // _.forEach(data.spec.metrics, (metric) => { - // if (metric.type === 'Resource') { - // if (metric.resource.name === 'cpu') { - // res.TargetCPUUtilization = metric.resource.target.averageUtilization; - // } - // if (metric.resource.name === 'memory') { - // res.TargetMemoryValue = parseFloat(metric.resource.target.averageValue) / 1000; - // } - // } - // }); - - // if (data.spec.scaleTargetRef) { - // res.TargetEntity.ApiVersion = data.spec.scaleTargetRef.apiVersion; - // res.TargetEntity.Kind = data.spec.scaleTargetRef.kind; - // res.TargetEntity.Name = data.spec.scaleTargetRef.name; - // } - // res.Yaml = yaml ? yaml.data : ''; - // return res; - // } - - // static createPayload(data) { - // const payload = new KubernetesHorizontalPodAutoScalerCreatePayload(); - // payload.metadata.namespace = data.Namespace; - // payload.metadata.name = data.TargetEntity.Name; - // payload.spec.minReplicas = data.MinReplicas; - // payload.spec.maxReplicas = data.MaxReplicas; - - // if (data.TargetMemoryValue) { - // const memoryMetric = new KubernetesHorizontalPodAutoScalerMemoryMetric(); - // memoryMetric.resource.target.averageValue = data.TargetMemoryValue; - // payload.spec.metrics.push(memoryMetric); - // } - - // if (data.TargetCPUUtilization) { - // const cpuMetric = new KubernetesHorizontalPodAutoScalerCPUMetric(); - // cpuMetric.resource.target.averageUtilization = data.TargetCPUUtilization; - // payload.spec.metrics.push(cpuMetric); - // } - - // payload.spec.scaleTargetRef.apiVersion = data.TargetEntity.ApiVersion; - // payload.spec.scaleTargetRef.kind = data.TargetEntity.Kind; - // payload.spec.scaleTargetRef.name = data.TargetEntity.Name; - - // return payload; - // } - - // static applicationFormValuesToModel(formValues, kind) { - // const res = new KubernetesHorizontalPodAutoScaler(); - // res.Name = formValues.Name; - // res.Namespace = formValues.ResourcePool.Namespace.Name; - // res.MinReplicas = formValues.AutoScaler.MinReplicas; - // res.MaxReplicas = formValues.AutoScaler.MaxReplicas; - // res.TargetCPUUtilization = formValues.AutoScaler.TargetCPUUtilization; - // if (formValues.AutoScaler.TargetMemoryValue) { - // res.TargetMemoryValue = formValues.AutoScaler.TargetMemoryValue + 'M'; - // } - // res.TargetEntity.Name = formValues.Name; - // res.TargetEntity.Kind = kind; - // return res; - // } } diff --git a/app/kubernetes/horizontal-pod-auto-scaler/helper.js b/app/kubernetes/horizontal-pod-auto-scaler/helper.js index 44bc10c49..43ce119c6 100644 --- a/app/kubernetes/horizontal-pod-auto-scaler/helper.js +++ b/app/kubernetes/horizontal-pod-auto-scaler/helper.js @@ -1,27 +1,8 @@ import _ from 'lodash-es'; -import PortainerError from 'Portainer/error'; -import { KubernetesApplication, KubernetesApplicationTypes, KubernetesApplicationTypeStrings } from 'Kubernetes/models/application/models'; -import { KubernetesDeployment } from 'Kubernetes/models/deployment/models'; -import { KubernetesStatefulSet } from 'Kubernetes/models/stateful-set/models'; -import { KubernetesDaemonSet } from 'Kubernetes/models/daemon-set/models'; export class KubernetesHorizontalPodAutoScalerHelper { static findApplicationBoundScaler(sList, app) { - const kind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(app); + const kind = app.ApplicationType; return _.find(sList, (item) => item.TargetEntity.Kind === kind && item.TargetEntity.Name === app.Name); } - - static getApplicationTypeString(app) { - if ((app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DEPLOYMENT) || app instanceof KubernetesDeployment) { - return KubernetesApplicationTypeStrings.DEPLOYMENT; - } else if ((app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DAEMONSET) || app instanceof KubernetesDaemonSet) { - return KubernetesApplicationTypeStrings.DAEMONSET; - } else if ((app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.STATEFULSET) || app instanceof KubernetesStatefulSet) { - return KubernetesApplicationTypeStrings.STATEFULSET; - } else if (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.POD) { - return KubernetesApplicationTypeStrings.POD; - } else { - throw new PortainerError('Unable to determine application type'); - } - } } diff --git a/app/kubernetes/ingress/converter.js b/app/kubernetes/ingress/converter.js index df04d7e06..897a2d19e 100644 --- a/app/kubernetes/ingress/converter.js +++ b/app/kubernetes/ingress/converter.js @@ -7,7 +7,6 @@ import { KubernetesResourcePoolIngressClassFormValue, KubernetesResourcePoolIngressClassHostFormValue, } from 'Kubernetes/models/resource-pool/formValues'; -import { KubernetesApplicationPublishingTypes } from '../models/application/models'; import { KubernetesIngress, KubernetesIngressRule } from './models'; import { KubernetesIngressCreatePayload, KubernetesIngressRuleCreatePayload, KubernetesIngressRulePathCreatePayload } from './payloads'; import { KubernetesIngressClassAnnotation, PortainerIngressClassTypes } from './constants'; @@ -48,36 +47,6 @@ export class KubernetesIngressConverter { return res; } - /** - * Converts Application Form Value (from Create Application View) to Ingresses - * @param {KubernetesApplicationFormValues} formValues - * @param {string} serviceName - * @returns {KubernetesIngressRule[]} - */ - static applicationFormValuesToIngresses(formValues, serviceName) { - const isPublishingToIngress = formValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS; - const ingresses = angular.copy(formValues.OriginalIngresses); - _.forEach(formValues.PublishedPorts, (p) => { - const ingress = _.find(ingresses, { Name: p.IngressName }); - if (ingress) { - if (p.NeedsDeletion) { - _.remove(ingress.Paths, (path) => path.Port === p.ContainerPort && path.ServiceName === serviceName && path.Path === p.IngressRoute); - } else if (isPublishingToIngress && p.IsNew) { - const rule = new KubernetesIngressRule(); - rule.IngressName = ingress.Name; - rule.ServiceName = serviceName; - rule.Port = p.ContainerPort; - if (p.IngressRoute) { - rule.Path = _.startsWith(p.IngressRoute, '/') ? p.IngressRoute : '/' + p.IngressRoute; - } - rule.Host = p.IngressHost; - ingress.Paths.push(rule); - } - } - }); - return ingresses; - } - static applicationFormValuesToDeleteIngresses(formValues, application) { const ingresses = angular.copy(formValues.OriginalIngresses); application.Services.forEach((service) => { diff --git a/app/kubernetes/models/application/formValues.js b/app/kubernetes/models/application/formValues.js index 57eefcade..b1cac46bc 100644 --- a/app/kubernetes/models/application/formValues.js +++ b/app/kubernetes/models/application/formValues.js @@ -1,11 +1,11 @@ import { PorImageRegistryModel } from '@/docker/models/porImageRegistry'; -import { KubernetesApplicationDataAccessPolicies, KubernetesApplicationDeploymentTypes } from './models'; +import { KubernetesApplicationTypes, KubernetesApplicationDeploymentTypes, KubernetesApplicationDataAccessPolicies } from 'Kubernetes/models/application/models/appConstants'; /** * KubernetesApplicationFormValues Model */ export function KubernetesApplicationFormValues() { - this.ApplicationType = undefined; // will only exist for formValues generated from Application (app edit situation; + this.ApplicationType = KubernetesApplicationTypes.Deployment; // will only exist for formValues generated from Application (app edit situation; this.ResourcePool = {}; this.Name = ''; this.StackName = ''; @@ -14,13 +14,13 @@ export function KubernetesApplicationFormValues() { this.Note = ''; this.MemoryLimit = 0; this.CpuLimit = 0; - this.DeploymentType = KubernetesApplicationDeploymentTypes.REPLICATED; + this.DeploymentType = KubernetesApplicationDeploymentTypes.Replicated; this.ReplicaCount = 1; this.AutoScaler = {}; this.Containers = []; this.Services = []; this.EnvironmentVariables = []; // KubernetesApplicationEnvironmentVariableFormValue lis; - this.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.ISOLATED; + this.DataAccessPolicy = KubernetesApplicationDataAccessPolicies.Isolated; this.PersistedFolders = []; // KubernetesApplicationPersistedFolderFormValue lis; this.ConfigMaps = []; this.Secrets = []; @@ -130,11 +130,11 @@ export function KubernetesApplicationPlacementFormValue() { * KubernetesApplicationAutoScalerFormValue Model */ const _KubernetesApplicationAutoScalerFormValue = Object.freeze({ - MinReplicas: 0, - MaxReplicas: 0, - TargetCPUUtilization: 50, - ApiVersion: '', - IsUsed: false, + minReplicas: 0, + maxReplicas: 0, + targetCpuUtilizationPercentage: 50, + apiVersion: '', + isUsed: false, }); export class KubernetesApplicationAutoScalerFormValue { diff --git a/app/kubernetes/models/application/models/appConstants.ts b/app/kubernetes/models/application/models/appConstants.ts new file mode 100644 index 000000000..658fc30c2 --- /dev/null +++ b/app/kubernetes/models/application/models/appConstants.ts @@ -0,0 +1,41 @@ +import { + AppType, + AppDataAccessPolicy, + DeploymentType, +} from '@/react/kubernetes/applications/types'; +import { ServiceType } from '@/react/kubernetes/services/types'; + +// The following constants are used by angular views and can be removed once they are no longer referenced +export const KubernetesApplicationTypes: Record = { + Deployment: 'Deployment', + StatefulSet: 'StatefulSet', + DaemonSet: 'DaemonSet', + Pod: 'Pod', + Helm: 'Helm', +} as const; + +export const KubernetesApplicationDeploymentTypes: Record< + DeploymentType, + DeploymentType +> = { + Global: 'Global', + Replicated: 'Replicated', +} as const; + +export const KubernetesApplicationDataAccessPolicies: Record< + AppDataAccessPolicy, + AppDataAccessPolicy +> = { + Isolated: 'Isolated', + Shared: 'Shared', +} as const; + +export const KubernetesApplicationServiceTypes: Record< + ServiceType, + ServiceType +> = { + ClusterIP: 'ClusterIP', + NodePort: 'NodePort', + LoadBalancer: 'LoadBalancer', + ExternalName: 'ExternalName', +} as const; diff --git a/app/kubernetes/models/application/models/constants.js b/app/kubernetes/models/application/models/constants.js index aa06f7f25..d4caab60e 100644 --- a/app/kubernetes/models/application/models/constants.js +++ b/app/kubernetes/models/application/models/constants.js @@ -1,35 +1,3 @@ -export const KubernetesApplicationDeploymentTypes = Object.freeze({ - REPLICATED: 1, - GLOBAL: 2, -}); - -export const KubernetesApplicationDataAccessPolicies = Object.freeze({ - SHARED: 1, - ISOLATED: 2, -}); - -export const KubernetesApplicationTypes = Object.freeze({ - DEPLOYMENT: 1, - DAEMONSET: 2, - STATEFULSET: 3, - POD: 4, - HELM: 5, -}); - -export const KubernetesApplicationTypeStrings = Object.freeze({ - HELM: 'Helm', - DEPLOYMENT: 'Deployment', - DAEMONSET: 'DaemonSet', - STATEFULSET: 'StatefulSet', - POD: 'Pod', -}); - -export const KubernetesApplicationPublishingTypes = Object.freeze({ - CLUSTER_IP: 1, - NODE_PORT: 2, - LOAD_BALANCER: 3, -}); - export const KubernetesApplicationQuotaDefaults = { CpuLimit: 0.1, MemoryLimit: 64, // MB diff --git a/app/kubernetes/react/components/index.ts b/app/kubernetes/react/components/index.ts index c4473c3b8..e5ef79c44 100644 --- a/app/kubernetes/react/components/index.ts +++ b/app/kubernetes/react/components/index.ts @@ -6,7 +6,6 @@ import { NamespacesSelector } from '@/react/kubernetes/cluster/RegistryAccessVie import { StorageAccessModeSelector } from '@/react/kubernetes/cluster/ConfigureView/ConfigureForm/StorageAccessModeSelector'; import { NamespaceAccessUsersSelector } from '@/react/kubernetes/namespaces/AccessView/NamespaceAccessUsersSelector'; import { RegistriesSelector } from '@/react/kubernetes/namespaces/components/RegistriesFormSection/RegistriesSelector'; -import { DataAccessPolicyFormSection } from '@/react/kubernetes/applications/CreateView/DataAccessPolicyFormSection'; import { KubeServicesForm } from '@/react/kubernetes/applications/CreateView/application-services/KubeServicesForm'; import { kubeServicesValidation } from '@/react/kubernetes/applications/CreateView/application-services/kubeServicesValidation'; import { AppDeploymentTypeFormSection } from '@/react/kubernetes/applications/CreateView/AppDeploymentTypeFormSection'; @@ -22,6 +21,7 @@ import { PlacementFormSection, placementValidation, } from '@/react/kubernetes/applications/components/PlacementFormSection'; +import { ApplicationSummarySection } from '@/react/kubernetes/applications/components/ApplicationSummarySection'; import { withFormValidation } from '@/react-tools/withFormValidation'; import { withCurrentUser } from '@/react-tools/withCurrentUser'; import { YAMLInspector } from '@/react/kubernetes/components/YAMLInspector'; @@ -33,6 +33,7 @@ import { SecretsFormSection } from '@/react/kubernetes/applications/components/C import { configurationsValidationSchema } from '@/react/kubernetes/applications/components/ConfigurationsFormSection/configurationValidationSchema'; import { ConfigMapsFormSection } from '@/react/kubernetes/applications/components/ConfigurationsFormSection/ConfigMapsFormSection'; import { PersistedFoldersFormSection } from '@/react/kubernetes/applications/components/PersistedFoldersFormSection'; +import { DataAccessPolicyFormSection } from '@/react/kubernetes/applications/CreateView/DataAccessPolicyFormSection'; import { persistedFoldersValidation } from '@/react/kubernetes/applications/components/PersistedFoldersFormSection/persistedFoldersValidation'; import { ResourceReservationFormSection, @@ -46,6 +47,7 @@ import { AutoScalingFormSection, autoScalingValidation, } from '@/react/kubernetes/applications/components/AutoScalingFormSection'; +import { withControlledInput } from '@/react-tools/withControlledInput'; import { EnvironmentVariablesFieldset } from '@@/form-components/EnvironmentVariablesFieldset'; @@ -112,7 +114,7 @@ export const ngModule = angular r2a(withUIRouter(withReactQuery(withCurrentUser(NodesDatatable))), []) ) .component( - 'dataAccessPolicyFormSection', + 'accessPolicyFormSection', r2a(DataAccessPolicyFormSection, [ 'value', 'onChange', @@ -174,6 +176,13 @@ export const ngModule = angular [] ) ) + .component( + 'applicationSummarySection', + r2a( + withUIRouter(withReactQuery(withCurrentUser(ApplicationSummarySection))), + ['formValues', 'oldFormValues'] + ) + ) .component( 'kubernetesApplicationsStacksDatatable', r2a(withUIRouter(withCurrentUser(ApplicationsStacksDatatable)), [ @@ -193,7 +202,9 @@ export const componentsModule = ngModule.name; withFormValidation( ngModule, - withUIRouter(withCurrentUser(withReactQuery(KubeServicesForm))), + withControlledInput( + withUIRouter(withCurrentUser(withReactQuery(KubeServicesForm))) + ), 'kubeServicesForm', ['values', 'onChange', 'appName', 'selector', 'isEditMode', 'namespace'], kubeServicesValidation @@ -201,7 +212,7 @@ withFormValidation( withFormValidation( ngModule, - EnvironmentVariablesFieldset, + withControlledInput(EnvironmentVariablesFieldset), 'kubeEnvironmentVariablesFieldset', ['canUndoDelete'], // use kubeEnvVarValidationSchema instead of envVarValidation to add a regex matches rule @@ -210,7 +221,9 @@ withFormValidation( withFormValidation( ngModule, - withUIRouter(withCurrentUser(withReactQuery(ConfigMapsFormSection))), + withControlledInput( + withUIRouter(withCurrentUser(withReactQuery(ConfigMapsFormSection))) + ), 'configMapsFormSection', ['values', 'onChange', 'namespace'], configurationsValidationSchema @@ -218,7 +231,9 @@ withFormValidation( withFormValidation( ngModule, - withUIRouter(withCurrentUser(withReactQuery(SecretsFormSection))), + withControlledInput( + withUIRouter(withCurrentUser(withReactQuery(SecretsFormSection))) + ), 'secretsFormSection', ['values', 'onChange', 'namespace'], configurationsValidationSchema @@ -240,7 +255,11 @@ withFormValidation( withFormValidation( ngModule, - withUIRouter(withCurrentUser(withReactQuery(ResourceReservationFormSection))), + withControlledInput( + withUIRouter( + withCurrentUser(withReactQuery(ResourceReservationFormSection)) + ) + ), 'resourceReservationFormSection', [ 'namespaceHasQuota', @@ -253,7 +272,9 @@ withFormValidation( withFormValidation( ngModule, - withUIRouter(withCurrentUser(withReactQuery(ReplicationFormSection))), + withControlledInput( + withUIRouter(withCurrentUser(withReactQuery(ReplicationFormSection))) + ), 'replicationFormSection', [ 'supportScalableReplicaDeployment', @@ -266,7 +287,9 @@ withFormValidation( withFormValidation( ngModule, - withUIRouter(withCurrentUser(withReactQuery(AutoScalingFormSection))), + withControlledInput( + withUIRouter(withCurrentUser(withReactQuery(AutoScalingFormSection))) + ), 'autoScalingFormSection', ['isMetricsEnabled'], autoScalingValidation diff --git a/app/kubernetes/services/applicationService.js b/app/kubernetes/services/applicationService.js index efd9217a7..c473f2604 100644 --- a/app/kubernetes/services/applicationService.js +++ b/app/kubernetes/services/applicationService.js @@ -2,12 +2,9 @@ import _ from 'lodash-es'; import angular from 'angular'; import PortainerError from 'Portainer/error'; -import { KubernetesApplication, KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models'; import KubernetesApplicationHelper from 'Kubernetes/helpers/application'; import KubernetesApplicationConverter from 'Kubernetes/converters/application'; -import { KubernetesDeployment } from 'Kubernetes/models/deployment/models'; import { KubernetesStatefulSet } from 'Kubernetes/models/stateful-set/models'; -import { KubernetesDaemonSet } from 'Kubernetes/models/daemon-set/models'; import KubernetesServiceHelper from 'Kubernetes/helpers/serviceHelper'; import { KubernetesHorizontalPodAutoScalerHelper } from 'Kubernetes/horizontal-pod-auto-scaler/helper'; import { KubernetesHorizontalPodAutoScalerConverter } from 'Kubernetes/horizontal-pod-auto-scaler/converter'; @@ -15,6 +12,7 @@ import KubernetesPodConverter from 'Kubernetes/pod/converter'; import { notifyError } from '@/portainer/services/notifications'; import { KubernetesIngressConverter } from 'Kubernetes/ingress/converter'; import { generateNewIngressesFromFormPaths } from '@/react/kubernetes/applications/CreateView/application-services/utils'; +import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; class KubernetesApplicationService { /* #region CONSTRUCTOR */ @@ -58,13 +56,13 @@ class KubernetesApplicationService { /* #region UTILS */ _getApplicationApiService(app) { let apiService; - if (app instanceof KubernetesDeployment || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DEPLOYMENT)) { + if (app.ApplicationType === KubernetesApplicationTypes.Deployment) { apiService = this.KubernetesDeploymentService; - } else if (app instanceof KubernetesDaemonSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DAEMONSET)) { + } else if (app.ApplicationType === KubernetesApplicationTypes.DaemonSet) { apiService = this.KubernetesDaemonSetService; - } else if (app instanceof KubernetesStatefulSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.STATEFULSET)) { + } else if (app.ApplicationType === KubernetesApplicationTypes.StatefulSet) { apiService = this.KubernetesStatefulSetService; - } else if (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.POD) { + } else if (app.ApplicationType === KubernetesApplicationTypes.Pod) { apiService = this.KubernetesPodService; } else { throw new PortainerError('Unable to determine which association to use to retrieve API Service'); @@ -257,8 +255,8 @@ class KubernetesApplicationService { await Promise.all(_.without(claimPromises, undefined)); } - if (formValues.AutoScaler.IsUsed && formValues.DeploymentType !== KubernetesApplicationDeploymentTypes.GLOBAL) { - const kind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(app); + if (formValues.AutoScaler.isUsed && formValues.DeploymentType !== KubernetesApplicationDeploymentTypes.Global) { + const kind = app.ApplicationType; const autoScaler = KubernetesHorizontalPodAutoScalerConverter.applicationFormValuesToModel(formValues, kind); await this.KubernetesHorizontalPodAutoScalerService.create(autoScaler); } @@ -378,16 +376,16 @@ class KubernetesApplicationService { } } - const newKind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(newApp); + const newKind = newApp.ApplicationType; const newAutoScaler = KubernetesHorizontalPodAutoScalerConverter.applicationFormValuesToModel(newFormValues, newKind); - if (!oldFormValues.AutoScaler.IsUsed) { - if (newFormValues.AutoScaler.IsUsed) { + if (!oldFormValues.AutoScaler.isUsed) { + if (newFormValues.AutoScaler.isUsed) { await this.KubernetesHorizontalPodAutoScalerService.create(newAutoScaler); } } else { - const oldKind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(oldApp); + const oldKind = oldApp.ApplicationType; const oldAutoScaler = KubernetesHorizontalPodAutoScalerConverter.applicationFormValuesToModel(oldFormValues, oldKind); - if (newFormValues.AutoScaler.IsUsed) { + if (newFormValues.AutoScaler.isUsed) { await this.KubernetesHorizontalPodAutoScalerService.patch(oldAutoScaler, newAutoScaler); } else { await this.KubernetesHorizontalPodAutoScalerService.delete(oldAutoScaler); diff --git a/app/kubernetes/views/applications/applicationsController.js b/app/kubernetes/views/applications/applicationsController.js index 9965373c9..5ac361176 100644 --- a/app/kubernetes/views/applications/applicationsController.js +++ b/app/kubernetes/views/applications/applicationsController.js @@ -3,7 +3,7 @@ import _ from 'lodash-es'; import KubernetesStackHelper from 'Kubernetes/helpers/stackHelper'; import KubernetesApplicationHelper from 'Kubernetes/helpers/application'; import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper'; -import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models'; +import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants'; import { KubernetesPortainerApplicationStackNameLabel } from 'Kubernetes/models/application/models'; import { confirmDelete } from '@@/modals/confirm'; import { getDeploymentOptions } from '@/react/portainer/environments/environment.service'; @@ -90,7 +90,7 @@ class KubernetesApplicationsController { let actionCount = selectedItems.length; for (const application of selectedItems) { try { - if (application.ApplicationType === KubernetesApplicationTypes.HELM) { + if (application.ApplicationType === KubernetesApplicationTypes.Helm) { await this.HelmService.uninstall(this.endpoint.Id, application); } else { await this.KubernetesApplicationService.delete(application); diff --git a/app/kubernetes/views/applications/create/createApplication.html b/app/kubernetes/views/applications/create/createApplication.html index 90093a1f8..6fdd2cf9d 100644 --- a/app/kubernetes/views/applications/create/createApplication.html +++ b/app/kubernetes/views/applications/create/createApplication.html @@ -103,9 +103,16 @@ >
- - + + +
Actions
+