2021-02-26 15:50:33 +00:00
|
|
|
import _ from 'lodash-es';
|
2020-07-05 23:21:03 +00:00
|
|
|
import angular from 'angular';
|
|
|
|
import PortainerError from 'Portainer/error';
|
|
|
|
|
2020-08-14 04:56:56 +00:00
|
|
|
import {
|
|
|
|
KubernetesApplication,
|
|
|
|
KubernetesApplicationDeploymentTypes,
|
|
|
|
KubernetesApplicationPublishingTypes,
|
|
|
|
KubernetesApplicationTypes,
|
|
|
|
} from 'Kubernetes/models/application/models';
|
2020-07-05 23:21:03 +00:00
|
|
|
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
|
|
|
import KubernetesApplicationRollbackHelper from 'Kubernetes/helpers/application/rollback';
|
|
|
|
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';
|
2020-08-04 22:08:11 +00:00
|
|
|
import { KubernetesHorizontalPodAutoScalerConverter } from 'Kubernetes/horizontal-pod-auto-scaler/converter';
|
2020-08-12 23:30:23 +00:00
|
|
|
import { KubernetesIngressConverter } from 'Kubernetes/ingress/converter';
|
2020-10-26 18:34:07 +00:00
|
|
|
import KubernetesPodConverter from 'Kubernetes/pod/converter';
|
2020-07-05 23:21:03 +00:00
|
|
|
|
|
|
|
class KubernetesApplicationService {
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #region CONSTRUCTOR */
|
2020-07-05 23:21:03 +00:00
|
|
|
/* @ngInject */
|
|
|
|
constructor(
|
|
|
|
$async,
|
|
|
|
Authentication,
|
|
|
|
KubernetesDeploymentService,
|
|
|
|
KubernetesDaemonSetService,
|
|
|
|
KubernetesStatefulSetService,
|
|
|
|
KubernetesServiceService,
|
|
|
|
KubernetesSecretService,
|
|
|
|
KubernetesPersistentVolumeClaimService,
|
|
|
|
KubernetesNamespaceService,
|
|
|
|
KubernetesPodService,
|
|
|
|
KubernetesHistoryService,
|
2020-07-14 20:45:19 +00:00
|
|
|
KubernetesHorizontalPodAutoScalerService,
|
|
|
|
KubernetesIngressService
|
2020-07-05 23:21:03 +00:00
|
|
|
) {
|
|
|
|
this.$async = $async;
|
|
|
|
this.Authentication = Authentication;
|
|
|
|
this.KubernetesDeploymentService = KubernetesDeploymentService;
|
|
|
|
this.KubernetesDaemonSetService = KubernetesDaemonSetService;
|
|
|
|
this.KubernetesStatefulSetService = KubernetesStatefulSetService;
|
|
|
|
this.KubernetesServiceService = KubernetesServiceService;
|
|
|
|
this.KubernetesSecretService = KubernetesSecretService;
|
|
|
|
this.KubernetesPersistentVolumeClaimService = KubernetesPersistentVolumeClaimService;
|
|
|
|
this.KubernetesNamespaceService = KubernetesNamespaceService;
|
|
|
|
this.KubernetesPodService = KubernetesPodService;
|
|
|
|
this.KubernetesHistoryService = KubernetesHistoryService;
|
|
|
|
this.KubernetesHorizontalPodAutoScalerService = KubernetesHorizontalPodAutoScalerService;
|
2020-07-14 20:45:19 +00:00
|
|
|
this.KubernetesIngressService = KubernetesIngressService;
|
2020-07-05 23:21:03 +00:00
|
|
|
|
|
|
|
this.getAsync = this.getAsync.bind(this);
|
|
|
|
this.getAllAsync = this.getAllAsync.bind(this);
|
|
|
|
this.createAsync = this.createAsync.bind(this);
|
|
|
|
this.patchAsync = this.patchAsync.bind(this);
|
|
|
|
this.patchPartialAsync = this.patchPartialAsync.bind(this);
|
|
|
|
this.rollbackAsync = this.rollbackAsync.bind(this);
|
|
|
|
this.deleteAsync = this.deleteAsync.bind(this);
|
|
|
|
}
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #endregion */
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #region UTILS */
|
2020-07-05 23:21:03 +00:00
|
|
|
_getApplicationApiService(app) {
|
|
|
|
let apiService;
|
|
|
|
if (app instanceof KubernetesDeployment || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DEPLOYMENT)) {
|
|
|
|
apiService = this.KubernetesDeploymentService;
|
|
|
|
} else if (app instanceof KubernetesDaemonSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DAEMONSET)) {
|
|
|
|
apiService = this.KubernetesDaemonSetService;
|
|
|
|
} else if (app instanceof KubernetesStatefulSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.STATEFULSET)) {
|
|
|
|
apiService = this.KubernetesStatefulSetService;
|
2020-12-17 00:05:31 +00:00
|
|
|
} else if (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.POD) {
|
|
|
|
apiService = this.KubernetesPodService;
|
2020-07-05 23:21:03 +00:00
|
|
|
} else {
|
2020-10-26 18:34:07 +00:00
|
|
|
throw new PortainerError('Unable to determine which association to use to retrieve API Service');
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
return apiService;
|
|
|
|
}
|
|
|
|
|
2020-08-12 23:30:23 +00:00
|
|
|
_generateIngressPatchPromises(oldIngresses, newIngresses) {
|
|
|
|
return _.map(newIngresses, (newIng) => {
|
|
|
|
const oldIng = _.find(oldIngresses, { Name: newIng.Name });
|
|
|
|
return this.KubernetesIngressService.patch(oldIng, newIng);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
/* #endregion */
|
|
|
|
|
|
|
|
/* #region GET */
|
2020-07-05 23:21:03 +00:00
|
|
|
async getAsync(namespace, name) {
|
2021-08-24 04:34:18 +00:00
|
|
|
const [deployment, daemonSet, statefulSet, pod, pods, autoScalers, ingresses] = await Promise.allSettled([
|
|
|
|
this.KubernetesDeploymentService.get(namespace, name),
|
|
|
|
this.KubernetesDaemonSetService.get(namespace, name),
|
|
|
|
this.KubernetesStatefulSetService.get(namespace, name),
|
|
|
|
this.KubernetesPodService.get(namespace, name),
|
|
|
|
this.KubernetesPodService.get(namespace),
|
|
|
|
this.KubernetesHorizontalPodAutoScalerService.get(namespace),
|
|
|
|
this.KubernetesIngressService.get(namespace),
|
|
|
|
]);
|
|
|
|
|
|
|
|
let rootItem;
|
|
|
|
let converterFunc;
|
|
|
|
if (deployment.status === 'fulfilled') {
|
|
|
|
rootItem = deployment;
|
|
|
|
converterFunc = KubernetesApplicationConverter.apiDeploymentToApplication;
|
|
|
|
} else if (daemonSet.status === 'fulfilled') {
|
|
|
|
rootItem = daemonSet;
|
|
|
|
converterFunc = KubernetesApplicationConverter.apiDaemonSetToApplication;
|
|
|
|
} else if (statefulSet.status === 'fulfilled') {
|
|
|
|
rootItem = statefulSet;
|
|
|
|
converterFunc = KubernetesApplicationConverter.apiStatefulSetToapplication;
|
|
|
|
} else if (pod.status === 'fulfilled') {
|
|
|
|
rootItem = pod;
|
|
|
|
converterFunc = KubernetesApplicationConverter.apiPodToApplication;
|
|
|
|
} else {
|
|
|
|
throw new PortainerError('Unable to determine which association to use to convert application');
|
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
const services = await this.KubernetesServiceService.get(namespace);
|
|
|
|
const boundService = KubernetesServiceHelper.findApplicationBoundService(services, rootItem.value.Raw);
|
|
|
|
const service = boundService ? await this.KubernetesServiceService.get(namespace, boundService.metadata.name) : {};
|
2022-01-16 19:37:46 +00:00
|
|
|
const boundServices = KubernetesServiceHelper.findApplicationBoundServices(services, rootItem.value.Raw);
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
const application = converterFunc(rootItem.value.Raw, pods.value, service.Raw, ingresses.value);
|
|
|
|
application.Yaml = rootItem.value.Yaml;
|
|
|
|
application.Raw = rootItem.value.Raw;
|
|
|
|
application.Pods = _.map(application.Pods, (item) => KubernetesPodConverter.apiToModel(item));
|
|
|
|
application.Containers = KubernetesApplicationHelper.associateContainersAndApplication(application);
|
2022-01-16 19:37:46 +00:00
|
|
|
application.Services = boundServices;
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
const boundScaler = KubernetesHorizontalPodAutoScalerHelper.findApplicationBoundScaler(autoScalers.value, application);
|
|
|
|
const scaler = boundScaler ? await this.KubernetesHorizontalPodAutoScalerService.get(namespace, boundScaler.Name) : undefined;
|
|
|
|
application.AutoScaler = scaler;
|
2022-01-16 19:37:46 +00:00
|
|
|
application.Ingresses = ingresses;
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
await this.KubernetesHistoryService.get(application);
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
if (service.Yaml) {
|
|
|
|
application.Yaml += '---\n' + service.Yaml;
|
|
|
|
}
|
|
|
|
if (scaler && scaler.Yaml) {
|
|
|
|
application.Yaml += '---\n' + scaler.Yaml;
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
2021-08-24 04:34:18 +00:00
|
|
|
// TODO: refactor @LP
|
|
|
|
// append ingress yaml ?
|
|
|
|
return application;
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async getAllAsync(namespace) {
|
2021-08-24 04:34:18 +00:00
|
|
|
const namespaces = namespace ? [namespace] : _.map(await this.KubernetesNamespaceService.get(), 'Name');
|
|
|
|
|
|
|
|
const convertToApplication = (item, converterFunc, services, pods, ingresses) => {
|
|
|
|
const service = KubernetesServiceHelper.findApplicationBoundService(services, item);
|
2022-01-16 19:37:46 +00:00
|
|
|
const servicesFound = KubernetesServiceHelper.findApplicationBoundServices(services, item);
|
2021-08-24 04:34:18 +00:00
|
|
|
const application = converterFunc(item, pods, service, ingresses);
|
|
|
|
application.Containers = KubernetesApplicationHelper.associateContainersAndApplication(application);
|
2022-01-16 19:37:46 +00:00
|
|
|
application.Services = servicesFound;
|
2021-08-24 04:34:18 +00:00
|
|
|
return application;
|
|
|
|
};
|
|
|
|
|
|
|
|
const res = await Promise.all(
|
|
|
|
_.map(namespaces, async (ns) => {
|
|
|
|
const [deployments, daemonSets, statefulSets, services, pods, ingresses, autoScalers] = await Promise.all([
|
|
|
|
this.KubernetesDeploymentService.get(ns),
|
|
|
|
this.KubernetesDaemonSetService.get(ns),
|
|
|
|
this.KubernetesStatefulSetService.get(ns),
|
|
|
|
this.KubernetesServiceService.get(ns),
|
|
|
|
this.KubernetesPodService.get(ns),
|
|
|
|
this.KubernetesIngressService.get(ns),
|
|
|
|
this.KubernetesHorizontalPodAutoScalerService.get(ns),
|
|
|
|
]);
|
|
|
|
|
|
|
|
const deploymentApplications = _.map(deployments, (item) =>
|
|
|
|
convertToApplication(item, KubernetesApplicationConverter.apiDeploymentToApplication, services, pods, ingresses)
|
|
|
|
);
|
|
|
|
const daemonSetApplications = _.map(daemonSets, (item) => convertToApplication(item, KubernetesApplicationConverter.apiDaemonSetToApplication, services, pods, ingresses));
|
|
|
|
const statefulSetApplications = _.map(statefulSets, (item) =>
|
|
|
|
convertToApplication(item, KubernetesApplicationConverter.apiStatefulSetToapplication, services, pods, ingresses)
|
|
|
|
);
|
|
|
|
|
|
|
|
const boundPods = _.concat(_.flatMap(deploymentApplications, 'Pods'), _.flatMap(daemonSetApplications, 'Pods'), _.flatMap(statefulSetApplications, 'Pods'));
|
|
|
|
const unboundPods = _.without(pods, ...boundPods);
|
|
|
|
const nakedPodsApplications = _.map(unboundPods, (item) => convertToApplication(item, KubernetesApplicationConverter.apiPodToApplication, services, pods, ingresses));
|
|
|
|
|
|
|
|
const applications = _.concat(deploymentApplications, daemonSetApplications, statefulSetApplications, nakedPodsApplications);
|
|
|
|
_.forEach(applications, (app) => {
|
|
|
|
app.Pods = _.map(app.Pods, (item) => KubernetesPodConverter.apiToModel(item));
|
|
|
|
});
|
|
|
|
await Promise.all(
|
|
|
|
_.forEach(applications, async (application) => {
|
|
|
|
const boundScaler = KubernetesHorizontalPodAutoScalerHelper.findApplicationBoundScaler(autoScalers, application);
|
|
|
|
const scaler = boundScaler ? await this.KubernetesHorizontalPodAutoScalerService.get(ns, boundScaler.Name) : undefined;
|
|
|
|
application.AutoScaler = scaler;
|
2022-01-16 19:37:46 +00:00
|
|
|
application.Ingresses = await this.KubernetesIngressService.get(ns);
|
2021-08-24 04:34:18 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
return applications;
|
|
|
|
})
|
|
|
|
);
|
|
|
|
return _.flatten(res);
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get(namespace, name) {
|
|
|
|
if (name) {
|
|
|
|
return this.$async(this.getAsync, namespace, name);
|
|
|
|
}
|
|
|
|
return this.$async(this.getAllAsync, namespace);
|
|
|
|
}
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #endregion */
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #region CREATE */
|
2020-07-05 23:21:03 +00:00
|
|
|
// TODO: review
|
|
|
|
// resource creation flow
|
|
|
|
// should we keep formValues > Resource_1 || Resource_2
|
|
|
|
// or should we switch to formValues > Composite > Resource_1 || Resource_2
|
2021-06-09 22:38:23 +00:00
|
|
|
/**
|
|
|
|
* NOTE: Keep this method flow in sync with `getCreatedApplicationResources` method in the `applicationService` file
|
|
|
|
* To synchronise with kubernetes resource creation summary output, any new resources created in this method should
|
|
|
|
* also be displayed in the summary output (getCreatedApplicationResources)
|
|
|
|
*/
|
2020-07-05 23:21:03 +00:00
|
|
|
async createAsync(formValues) {
|
2022-01-16 19:37:46 +00:00
|
|
|
// formValues -> Application
|
|
|
|
let [app, headlessService, services, service, claims] = KubernetesApplicationConverter.applicationFormValuesToApplication(formValues);
|
|
|
|
|
|
|
|
if (services) {
|
|
|
|
services.forEach(async (service) => {
|
2022-03-16 21:25:36 +00:00
|
|
|
await this.KubernetesServiceService.create(service);
|
2022-01-16 19:37:46 +00:00
|
|
|
});
|
2022-03-16 21:25:36 +00:00
|
|
|
|
|
|
|
//Generate all ingresses from current form by passing services object
|
|
|
|
const ingresses = KubernetesIngressConverter.generateNewIngresses(formValues.OriginalIngresses, services);
|
|
|
|
if (ingresses) {
|
|
|
|
//Update original ingress with current ingress
|
|
|
|
await Promise.all(this._generateIngressPatchPromises(formValues.OriginalIngresses, ingresses));
|
|
|
|
}
|
2022-01-16 19:37:46 +00:00
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
if (service) {
|
|
|
|
if (formValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS) {
|
|
|
|
const ingresses = KubernetesIngressConverter.applicationFormValuesToIngresses(formValues, service.Name);
|
|
|
|
await Promise.all(this._generateIngressPatchPromises(formValues.OriginalIngresses, ingresses));
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
2021-08-24 04:34:18 +00:00
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
const apiService = this._getApplicationApiService(app);
|
2020-08-04 22:08:11 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
if (app instanceof KubernetesStatefulSet) {
|
|
|
|
app.VolumeClaims = claims;
|
|
|
|
headlessService = await this.KubernetesServiceService.create(headlessService);
|
|
|
|
app.ServiceName = headlessService.metadata.name;
|
|
|
|
} else {
|
|
|
|
const claimPromises = _.map(claims, (item) => {
|
|
|
|
if (!item.PreviousName && !item.Id) {
|
|
|
|
return this.KubernetesPersistentVolumeClaimService.create(item);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
await Promise.all(_.without(claimPromises, undefined));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (formValues.AutoScaler.IsUsed && formValues.DeploymentType !== KubernetesApplicationDeploymentTypes.GLOBAL) {
|
|
|
|
const kind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(app);
|
|
|
|
const autoScaler = KubernetesHorizontalPodAutoScalerConverter.applicationFormValuesToModel(formValues, kind);
|
|
|
|
await this.KubernetesHorizontalPodAutoScalerService.create(autoScaler);
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
2021-08-24 04:34:18 +00:00
|
|
|
|
|
|
|
await apiService.create(app);
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
create(formValues) {
|
|
|
|
return this.$async(this.createAsync, formValues);
|
|
|
|
}
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #endregion */
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #region PATCH */
|
2020-07-05 23:21:03 +00:00
|
|
|
// this function accepts KubernetesApplicationFormValues as parameters
|
2021-06-09 22:38:23 +00:00
|
|
|
/**
|
|
|
|
* NOTE: Keep this method flow in sync with `getUpdatedApplicationResources` method in the `applicationService` file
|
|
|
|
* To synchronise with kubernetes resource creation, update and delete summary output, any new resources created
|
|
|
|
* in this method should also be displayed in the summary output (getUpdatedApplicationResources)
|
|
|
|
*/
|
2020-07-05 23:21:03 +00:00
|
|
|
async patchAsync(oldFormValues, newFormValues) {
|
2022-05-30 22:12:37 +00:00
|
|
|
const [oldApp, oldHeadlessService, oldServices, , oldClaims] = KubernetesApplicationConverter.applicationFormValuesToApplication(oldFormValues);
|
|
|
|
const [newApp, newHeadlessService, newServices, , newClaims] = KubernetesApplicationConverter.applicationFormValuesToApplication(newFormValues);
|
2021-08-24 04:34:18 +00:00
|
|
|
const oldApiService = this._getApplicationApiService(oldApp);
|
|
|
|
const newApiService = this._getApplicationApiService(newApp);
|
|
|
|
|
|
|
|
if (oldApiService !== newApiService) {
|
2022-05-30 22:12:37 +00:00
|
|
|
// delete services first
|
|
|
|
if (oldServices) {
|
|
|
|
await this.KubernetesServiceService.delete(oldServices);
|
2022-01-16 19:37:46 +00:00
|
|
|
}
|
2022-05-30 22:12:37 +00:00
|
|
|
|
|
|
|
// delete the app
|
|
|
|
await this.delete(oldApp);
|
|
|
|
|
|
|
|
// sleep for 5 seconds to allow the app/services to be deleted
|
|
|
|
await new Promise((r) => setTimeout(r, 5000));
|
|
|
|
|
|
|
|
// create the app
|
2021-08-24 04:34:18 +00:00
|
|
|
return await this.create(newFormValues);
|
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
if (newApp instanceof KubernetesStatefulSet) {
|
|
|
|
await this.KubernetesServiceService.patch(oldHeadlessService, newHeadlessService);
|
|
|
|
} else {
|
|
|
|
const claimPromises = _.map(newClaims, (newClaim) => {
|
|
|
|
if (!newClaim.PreviousName && !newClaim.Id) {
|
|
|
|
return this.KubernetesPersistentVolumeClaimService.create(newClaim);
|
|
|
|
} else if (!newClaim.Id) {
|
|
|
|
const oldClaim = _.find(oldClaims, { Name: newClaim.PreviousName });
|
|
|
|
return this.KubernetesPersistentVolumeClaimService.patch(oldClaim, newClaim);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
await Promise.all(claimPromises);
|
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
await newApiService.patch(oldApp, newApp);
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2022-03-16 21:25:36 +00:00
|
|
|
// Create services
|
2022-01-16 19:37:46 +00:00
|
|
|
if (oldServices.length === 0 && newServices.length !== 0) {
|
|
|
|
newServices.forEach(async (service) => {
|
|
|
|
await this.KubernetesServiceService.create(service);
|
|
|
|
});
|
2022-03-16 21:25:36 +00:00
|
|
|
|
|
|
|
// Create multiple ingress
|
|
|
|
const ingresses = KubernetesIngressConverter.generateNewIngresses(oldFormValues.OriginalIngresses, newServices);
|
|
|
|
if (ingresses) {
|
|
|
|
await Promise.all(this._generateIngressPatchPromises(oldFormValues.OriginalIngresses, ingresses));
|
|
|
|
}
|
2022-01-16 19:37:46 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 21:25:36 +00:00
|
|
|
// Delete services ( only called when all services been deleted )
|
2022-01-16 19:37:46 +00:00
|
|
|
if (oldServices.length !== 0 && newServices.length === 0) {
|
2022-03-16 21:25:36 +00:00
|
|
|
const ingresses = KubernetesIngressConverter.removeIngressesPaths(oldFormValues.OriginalIngresses, oldServices);
|
|
|
|
if (ingresses) {
|
|
|
|
await Promise.all(this._generateIngressPatchPromises(oldFormValues.OriginalIngresses, ingresses));
|
|
|
|
}
|
2022-01-16 19:37:46 +00:00
|
|
|
await this.KubernetesServiceService.deleteAll(oldServices);
|
|
|
|
}
|
|
|
|
|
2022-03-16 21:25:36 +00:00
|
|
|
// Patch services ( Action including: Delete, Update, Create )
|
2022-01-16 19:37:46 +00:00
|
|
|
if (oldServices.length !== 0 && newServices.length !== 0) {
|
2022-03-16 21:25:36 +00:00
|
|
|
oldServices.forEach(async (oldService) => {
|
|
|
|
const newServiceMatched = _.find(newServices, { Name: oldService.Name });
|
|
|
|
if (!newServiceMatched) {
|
|
|
|
await this.KubernetesServiceService.deleteSingle(oldService);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-01-16 19:37:46 +00:00
|
|
|
newServices.forEach(async (newService) => {
|
|
|
|
const oldServiceMatched = _.find(oldServices, { Name: newService.Name });
|
|
|
|
if (oldServiceMatched) {
|
|
|
|
await this.KubernetesServiceService.patch(oldServiceMatched, newService);
|
|
|
|
} else {
|
|
|
|
await this.KubernetesServiceService.create(newService);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-03-16 21:25:36 +00:00
|
|
|
// Clear all ingress which is related to services in this application
|
|
|
|
const clearIngress = KubernetesIngressConverter.removeIngressesPaths(oldFormValues.OriginalIngresses, oldServices);
|
|
|
|
|
|
|
|
// Generate all ingress from services in this application
|
|
|
|
const newIngress = KubernetesIngressConverter.generateNewIngresses(clearIngress, newServices);
|
|
|
|
|
|
|
|
// Compare new ingress with old ingress to get api patch
|
|
|
|
await Promise.all(this._generateIngressPatchPromises(oldFormValues.OriginalIngresses, newIngress));
|
2021-08-24 04:34:18 +00:00
|
|
|
}
|
2020-08-04 22:08:11 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
const newKind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(newApp);
|
|
|
|
const newAutoScaler = KubernetesHorizontalPodAutoScalerConverter.applicationFormValuesToModel(newFormValues, newKind);
|
|
|
|
if (!oldFormValues.AutoScaler.IsUsed) {
|
|
|
|
if (newFormValues.AutoScaler.IsUsed) {
|
|
|
|
await this.KubernetesHorizontalPodAutoScalerService.create(newAutoScaler);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const oldKind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(oldApp);
|
|
|
|
const oldAutoScaler = KubernetesHorizontalPodAutoScalerConverter.applicationFormValuesToModel(oldFormValues, oldKind);
|
|
|
|
if (newFormValues.AutoScaler.IsUsed) {
|
|
|
|
await this.KubernetesHorizontalPodAutoScalerService.patch(oldAutoScaler, newAutoScaler);
|
2020-08-04 22:08:11 +00:00
|
|
|
} else {
|
2021-08-24 04:34:18 +00:00
|
|
|
await this.KubernetesHorizontalPodAutoScalerService.delete(oldAutoScaler);
|
2020-08-04 22:08:11 +00:00
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// this function accepts KubernetesApplication as parameters
|
|
|
|
async patchPartialAsync(oldApp, newApp) {
|
2021-08-24 04:34:18 +00:00
|
|
|
const oldAppPayload = {
|
|
|
|
Name: oldApp.Name,
|
|
|
|
Namespace: oldApp.ResourcePool,
|
|
|
|
StackName: oldApp.StackName,
|
|
|
|
Note: oldApp.Note,
|
|
|
|
};
|
|
|
|
const newAppPayload = {
|
|
|
|
Name: newApp.Name,
|
|
|
|
Namespace: newApp.ResourcePool,
|
|
|
|
StackName: newApp.StackName,
|
|
|
|
Note: newApp.Note,
|
|
|
|
};
|
|
|
|
const apiService = this._getApplicationApiService(oldApp);
|
|
|
|
await apiService.patch(oldAppPayload, newAppPayload);
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
|
2021-11-30 04:14:52 +00:00
|
|
|
// accept either formValues or applications as parameters depending on partial value
|
2020-07-05 23:21:03 +00:00
|
|
|
// true = KubernetesApplication
|
|
|
|
// false = KubernetesApplicationFormValues
|
2021-11-30 04:14:52 +00:00
|
|
|
//
|
|
|
|
// e.g. signatures are
|
|
|
|
//
|
|
|
|
// patch(oldValues: KubernetesApplication, newValues: KubernetesApplication, partial: (undefined | false)): Promise<unknown>
|
|
|
|
// patch(oldValues: KubernetesApplicationFormValues, newValues: KubernetesApplicationFormValues, partial: true): Promise<unknown>
|
2020-07-05 23:21:03 +00:00
|
|
|
patch(oldValues, newValues, partial = false) {
|
|
|
|
if (partial) {
|
|
|
|
return this.$async(this.patchPartialAsync, oldValues, newValues);
|
|
|
|
}
|
|
|
|
return this.$async(this.patchAsync, oldValues, newValues);
|
|
|
|
}
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #endregion */
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #region DELETE */
|
2020-07-05 23:21:03 +00:00
|
|
|
async deleteAsync(application) {
|
2021-08-24 04:34:18 +00:00
|
|
|
const payload = {
|
|
|
|
Namespace: application.ResourcePool || application.Namespace,
|
|
|
|
Name: application.Name,
|
|
|
|
};
|
|
|
|
const servicePayload = angular.copy(payload);
|
|
|
|
servicePayload.Name = application.Name;
|
|
|
|
|
|
|
|
const apiService = this._getApplicationApiService(application);
|
|
|
|
await apiService.delete(payload);
|
|
|
|
|
|
|
|
if (apiService === this.KubernetesStatefulSetService) {
|
|
|
|
const headlessServicePayload = angular.copy(payload);
|
|
|
|
headlessServicePayload.Name = application instanceof KubernetesStatefulSet ? application.ServiceName : application.HeadlessServiceName;
|
|
|
|
}
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
if (application.ServiceType) {
|
2022-04-05 17:42:01 +00:00
|
|
|
// delete headless service && non-headless service
|
2022-01-16 19:37:46 +00:00
|
|
|
await this.KubernetesServiceService.delete(application.Services);
|
|
|
|
|
|
|
|
if (application.Ingresses.length) {
|
2021-08-24 04:34:18 +00:00
|
|
|
const originalIngresses = await this.KubernetesIngressService.get(payload.Namespace);
|
|
|
|
const formValues = {
|
|
|
|
OriginalIngresses: originalIngresses,
|
|
|
|
PublishedPorts: KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(application.ServiceType, application.PublishedPorts),
|
|
|
|
};
|
2022-01-16 19:37:46 +00:00
|
|
|
const ingresses = KubernetesIngressConverter.applicationFormValuesToDeleteIngresses(formValues, application);
|
|
|
|
|
2021-08-24 04:34:18 +00:00
|
|
|
await Promise.all(this._generateIngressPatchPromises(formValues.OriginalIngresses, ingresses));
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
2021-08-24 04:34:18 +00:00
|
|
|
}
|
|
|
|
if (!_.isEmpty(application.AutoScaler)) {
|
|
|
|
await this.KubernetesHorizontalPodAutoScalerService.delete(application.AutoScaler);
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(application) {
|
|
|
|
return this.$async(this.deleteAsync, application);
|
|
|
|
}
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #endregion */
|
2020-07-05 23:21:03 +00:00
|
|
|
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #region ROLLBACK */
|
2020-07-05 23:21:03 +00:00
|
|
|
async rollbackAsync(application, targetRevision) {
|
2021-08-24 04:34:18 +00:00
|
|
|
const payload = KubernetesApplicationRollbackHelper.getPatchPayload(application, targetRevision);
|
|
|
|
const apiService = this._getApplicationApiService(application);
|
|
|
|
await apiService.rollback(application.ResourcePool, application.Name, payload);
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rollback(application, targetRevision) {
|
|
|
|
return this.$async(this.rollbackAsync, application, targetRevision);
|
|
|
|
}
|
2020-08-12 23:30:23 +00:00
|
|
|
/* #endregion */
|
2020-07-05 23:21:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export default KubernetesApplicationService;
|
|
|
|
angular.module('portainer.kubernetes').service('KubernetesApplicationService', KubernetesApplicationService);
|