1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-21 06:19:41 +02:00

feat(k8s/applications): expose applications via ingress (#4136)

* feat(k8s/endpoint): expose ingress controllers on endpoints

* feat(k8s/applications): add ability to expose applications over ingress - missing RP and app edits

* feat(k8s/application): add validation for ingress routes

* feat(k8s/resource-pools): edit available ingress classes

* fix(k8s/ingress): var name refactor was partially applied

* feat(kubernetes): double validation on RP edit

* feat(k8s/application): app edit ingress update + formvalidation + UI rework

* feat(k8s/ingress): dictionary for default annotations on ingress creation

* fix(k8s/application): temporary fix + TODO dev notice

* feat(k8s/application): select default ingress of selected resource pool

* feat(k8s/ingress): revert ingressClassName removal

* feat(k8s/ingress): admins can now add an host to ingress in a resource pool

* feat(k8s/resource-pool): list applications using RP ingresses

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* feat(k8s/configure): minor UI update

* fix(k8s/ingresses): remove host if undefined

* feat(k8s/resource-pool): remove the activate ingresses switch

* fix(k8s/resource-pool): edditing an ingress host was deleting all the routes of the ingress

* feat(k8s/application): prevent app deploy if no ports to publish and publishing type not internal

* feat(k8s/ingress): minor UI update

* fix(k8s/ingress): allow routes without prepending /

* feat(k8s/application): add form validation on ingress route

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
This commit is contained in:
xAt0mZ 2020-08-13 01:30:23 +02:00 committed by GitHub
parent 201c3ac143
commit f91d3f1ca3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 1595 additions and 443 deletions

View file

@ -1,8 +1,8 @@
import _ from 'lodash-es';
import * as _ from 'lodash-es';
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
import { KubernetesApplicationTypes, KubernetesApplicationPublishingTypes } from 'Kubernetes/models/application/models';
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
import KubernetesApplicationRollbackHelper from 'Kubernetes/helpers/application/rollback';
import KubernetesApplicationConverter from 'Kubernetes/converters/application';
@ -13,8 +13,10 @@ import { KubernetesApplication } from 'Kubernetes/models/application/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';
import { KubernetesIngressConverter } from 'Kubernetes/ingress/converter';
class KubernetesApplicationService {
/* #region CONSTRUCTOR */
/* @ngInject */
constructor(
$async,
@ -53,10 +55,9 @@ class KubernetesApplicationService {
this.rollbackAsync = this.rollbackAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/* #endregion */
/**
* UTILS
*/
/* #region UTILS */
_getApplicationApiService(app) {
let apiService;
if (app instanceof KubernetesDeployment || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DEPLOYMENT)) {
@ -71,9 +72,15 @@ class KubernetesApplicationService {
return apiService;
}
/**
* GET
*/
_generateIngressPatchPromises(oldIngresses, newIngresses) {
return _.map(newIngresses, (newIng) => {
const oldIng = _.find(oldIngresses, { Name: newIng.Name });
return this.KubernetesIngressService.patch(oldIng, newIng);
});
}
/* #endregion */
/* #region GET */
async getAsync(namespace, name) {
try {
const [deployment, daemonSet, statefulSet, pods, autoScalers, ingresses] = await Promise.allSettled([
@ -121,7 +128,7 @@ class KubernetesApplicationService {
if (scaler && scaler.Yaml) {
application.Yaml += '---\n' + scaler.Yaml;
}
// TODO: refactor
// TODO: refactor @LP
// append ingress yaml ?
return application;
} catch (err) {
@ -185,10 +192,9 @@ class KubernetesApplicationService {
}
return this.$async(this.getAllAsync, namespace);
}
/* #endregion */
/**
* CREATE
*/
/* #region CREATE */
// TODO: review
// resource creation flow
// should we keep formValues > Resource_1 || Resource_2
@ -199,6 +205,10 @@ class KubernetesApplicationService {
if (service) {
await this.KubernetesServiceService.create(service);
if (formValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS) {
const ingresses = KubernetesIngressConverter.applicationFormValuesToIngresses(formValues, service.Name);
await Promise.all(this._generateIngressPatchPromises(formValues.OriginalIngresses, ingresses));
}
}
const apiService = this._getApplicationApiService(app);
@ -231,10 +241,9 @@ class KubernetesApplicationService {
create(formValues) {
return this.$async(this.createAsync, formValues);
}
/* #endregion */
/**
* PATCH
*/
/* #region PATCH */
// this function accepts KubernetesApplicationFormValues as parameters
async patchAsync(oldFormValues, newFormValues) {
try {
@ -269,10 +278,23 @@ class KubernetesApplicationService {
if (oldService && newService) {
await this.KubernetesServiceService.patch(oldService, newService);
if (newFormValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS || oldFormValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS) {
const oldIngresses = KubernetesIngressConverter.applicationFormValuesToIngresses(oldFormValues, oldService.Name);
const newIngresses = KubernetesIngressConverter.applicationFormValuesToIngresses(newFormValues, newService.Name);
await Promise.all(this._generateIngressPatchPromises(oldIngresses, newIngresses));
}
} else if (!oldService && newService) {
await this.KubernetesServiceService.create(newService);
if (newFormValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS) {
const ingresses = KubernetesIngressConverter.applicationFormValuesToIngresses(newFormValues, newService.Name);
await Promise.all(this._generateIngressPatchPromises(newFormValues.OriginalIngresses, ingresses));
}
} else if (oldService && !newService) {
await this.KubernetesServiceService.delete(oldService);
if (oldFormValues.PublishingType === KubernetesApplicationPublishingTypes.INGRESS) {
const ingresses = KubernetesIngressConverter.applicationFormValuesToIngresses(newFormValues, oldService.Name);
await Promise.all(this._generateIngressPatchPromises(oldFormValues.OriginalIngresses, ingresses));
}
}
const newKind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(newApp);
@ -327,10 +349,9 @@ class KubernetesApplicationService {
}
return this.$async(this.patchAsync, oldValues, newValues);
}
/* #endregion */
/**
* DELETE
*/
/* #region DELETE */
async deleteAsync(application) {
try {
const payload = {
@ -351,8 +372,18 @@ class KubernetesApplicationService {
if (application.ServiceType) {
await this.KubernetesServiceService.delete(servicePayload);
const isIngress = _.filter(application.PublishedPorts, (p) => p.IngressRules.length).length;
if (isIngress) {
const originalIngresses = await this.KubernetesIngressService.get(payload.Namespace);
const formValues = {
OriginalIngresses: originalIngresses,
PublishedPorts: KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(application.ServiceType, application.PublishedPorts),
};
_.forEach(formValues.PublishedPorts, (p) => (p.NeedsDeletion = true));
const ingresses = KubernetesIngressConverter.applicationFormValuesToIngresses(formValues, servicePayload.Name);
await Promise.all(this._generateIngressPatchPromises(formValues.OriginalIngresses, ingresses));
}
}
if (!_.isEmpty(application.AutoScaler)) {
await this.KubernetesHorizontalPodAutoScalerService.delete(application.AutoScaler);
}
@ -364,10 +395,9 @@ class KubernetesApplicationService {
delete(application) {
return this.$async(this.deleteAsync, application);
}
/* #endregion */
/**
* ROLLBACK
*/
/* #region ROLLBACK */
async rollbackAsync(application, targetRevision) {
try {
const payload = KubernetesApplicationRollbackHelper.getPatchPayload(application, targetRevision);
@ -381,6 +411,7 @@ class KubernetesApplicationService {
rollback(application, targetRevision) {
return this.$async(this.rollbackAsync, application, targetRevision);
}
/* #endregion */
}
export default KubernetesApplicationService;