1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

feat(k8s/ingresses): add more granularity to ingress configuration (#4220)

* feat(k8s/configure): separate ingress class name and ingress class type

* feat(k8s/resource-pool): ability to add custom annotations to ingress classes on RP create/edit

* feat(k8s/ingresses): remove 'allow users to use ingress' switch

* feat(k8s/configure): minor UI update

* feat(k8s/resource-pool): minor UI update

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

* refactor(k8s/resource-pool): remove console.log statement

* feat(k8s/resource-pool): update ingress annotation placeholders

* feat(k8s/configure): add pattern form validation on ingress class

* fix(k8s/resource-pool): automatically associate ingress class to ingress

* fix(k8s/resource-pool): fix invalid ingress when updating a resource pool

* fix(k8s/resource-pool): update ingress rewrite target annotation value

* feat(k8s/application): ingress form validation

* fix(k8s/application): squash ingress rules with empty host inside a single one

* feat(k8s/resource-pool): ingress host validation

* fix(k8s/resource-pool): rewrite rewrite option and only display it for ingress of type nginx

* feat(k8s/application): do not expose ingress applications over node port

* feat(k8s/application): add specific notice for ingress

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
This commit is contained in:
xAt0mZ 2020-08-20 02:51:14 +02:00 committed by GitHub
parent 68851aada4
commit d850e18ff0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 699 additions and 220 deletions

View file

@ -1,4 +1,13 @@
export const KubernetesIngressClassAnnotation = 'kubernetes.io/ingress.class';
export const KubernetesIngressClassMandatoryAnnotations = Object.freeze({
nginx: { 'nginx.ingress.kubernetes.io/rewrite-target': '/$1' },
// keys must match KubernetesIngressClassTypes values to map them quickly using the ingress type
// KubernetesIngressClassRewriteTargetAnnotations[KubernetesIngressClassTypes.NGINX] for example
export const KubernetesIngressClassRewriteTargetAnnotations = Object.freeze({
nginx: { 'nginx.ingress.kubernetes.io/rewrite-target': '/' },
traefik: { 'traefik.ingress.kubernetes.io/rewrite-target': '/' },
});
export const KubernetesIngressClassTypes = Object.freeze({
NGINX: 'nginx',
TRAEFIK: 'traefik',
});

View file

@ -2,9 +2,10 @@ import * as _ from 'lodash-es';
import * as JsonPatch from 'fast-json-patch';
import KubernetesCommonHelper from 'Kubernetes/helpers/commonHelper';
import { KubernetesIngressRule, KubernetesIngress } from './models';
import { KubernetesResourcePoolIngressClassAnnotationFormValue, KubernetesResourcePoolIngressClassFormValue } from 'Kubernetes/models/resource-pool/formValues';
import { KubernetesIngress, KubernetesIngressRule } from './models';
import { KubernetesIngressCreatePayload, KubernetesIngressRuleCreatePayload, KubernetesIngressRulePathCreatePayload } from './payloads';
import { KubernetesIngressClassAnnotation, KubernetesIngressClassMandatoryAnnotations } from './constants';
import { KubernetesIngressClassAnnotation, KubernetesIngressClassRewriteTargetAnnotations } from './constants';
export class KubernetesIngressConverter {
// TODO: refactor @LP
@ -64,17 +65,68 @@ export class KubernetesIngressConverter {
return ingresses;
}
/**
*
* @param {KubernetesResourcePoolIngressClassFormValue} formValues
*/
static resourcePoolIngressClassFormValueToIngress(formValues) {
const res = new KubernetesIngress();
res.Name = formValues.IngressClass.Name;
res.Namespace = formValues.Namespace;
const pairs = _.map(formValues.Annotations, (a) => [a.Key, a.Value]);
res.Annotations = _.fromPairs(pairs);
if (formValues.RewriteTarget) {
_.extend(res.Annotations, KubernetesIngressClassRewriteTargetAnnotations[formValues.IngressClass.Type]);
}
res.Annotations[KubernetesIngressClassAnnotation] = formValues.IngressClass.Name;
res.Host = formValues.Host;
return res;
}
/**
*
* @param {KubernetesIngressClass} ics Ingress classes (saved in Portainer DB)
* @param {KubernetesIngress} ingresses Existing Kubernetes ingresses. Must be empty for RP CREATE VIEW and passed for RP EDIT VIEW
*/
static ingressClassesToFormValues(ics, ingresses) {
const res = _.map(ics, (ic) => {
const fv = new KubernetesResourcePoolIngressClassFormValue();
fv.IngressClass = ic;
const ingress = _.find(ingresses, { Name: ic.Name });
if (ingress) {
fv.Selected = true;
fv.WasSelected = true;
fv.Host = ingress.Host;
const [[rewriteKey]] = _.toPairs(KubernetesIngressClassRewriteTargetAnnotations[ic.Type]);
const annotations = _.map(_.toPairs(ingress.Annotations), ([key, value]) => {
if (key === rewriteKey) {
fv.RewriteTarget = true;
} else if (key !== KubernetesIngressClassAnnotation) {
const annotation = new KubernetesResourcePoolIngressClassAnnotationFormValue();
annotation.Key = key;
annotation.Value = value;
return annotation;
}
});
fv.Annotations = _.without(annotations, undefined);
fv.AdvancedConfig = fv.Annotations.length > 0;
}
return fv;
});
return res;
}
static createPayload(data) {
const res = new KubernetesIngressCreatePayload();
res.metadata.name = data.Name;
res.metadata.namespace = data.Namespace;
res.metadata.annotations = data.Annotations || {};
res.metadata.annotations[KubernetesIngressClassAnnotation] = data.IngressClassName;
const annotations = KubernetesIngressClassMandatoryAnnotations[data.Name];
if (annotations) {
_.extend(res.metadata.annotations, annotations);
}
res.metadata.annotations = data.Annotations;
if (data.Paths && data.Paths.length) {
_.forEach(data.Paths, (p) => {
if (p.Host === 'undefined' || p.Host === undefined) {
p.Host = '';
}
});
const groups = _.groupBy(data.Paths, 'Host');
const rules = _.map(groups, (paths, host) => {
const rule = new KubernetesIngressRuleCreatePayload();

View file

@ -24,3 +24,12 @@ export function KubernetesIngressRule() {
Path: '',
};
}
export function KubernetesIngressClass() {
return {
Name: '',
Type: undefined,
NeedsDeletion: false,
IsNew: true,
};
}

View file

@ -55,10 +55,10 @@ class KubernetesIngressService {
/**
* CREATE
*/
async createAsync(formValues) {
async createAsync(ingress) {
try {
const params = {};
const payload = KubernetesIngressConverter.createPayload(formValues);
const payload = KubernetesIngressConverter.createPayload(ingress);
const namespace = payload.metadata.namespace;
const data = await this.KubernetesIngresses(namespace).create(params, payload).$promise;
return data;
@ -67,8 +67,8 @@ class KubernetesIngressService {
}
}
create(formValues) {
return this.$async(this.createAsync, formValues);
create(ingress) {
return this.$async(this.createAsync, ingress);
}
/**
@ -100,7 +100,7 @@ class KubernetesIngressService {
async deleteAsync(ingress) {
try {
const params = new KubernetesCommonParams();
params.id = ingress.Name;
params.id = ingress.IngressClass.Name;
const namespace = ingress.Namespace;
await this.KubernetesIngresses(namespace).delete(params).$promise;
} catch (err) {