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:
parent
68851aada4
commit
d850e18ff0
21 changed files with 699 additions and 220 deletions
|
@ -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',
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -24,3 +24,12 @@ export function KubernetesIngressRule() {
|
|||
Path: '',
|
||||
};
|
||||
}
|
||||
|
||||
export function KubernetesIngressClass() {
|
||||
return {
|
||||
Name: '',
|
||||
Type: undefined,
|
||||
NeedsDeletion: false,
|
||||
IsNew: true,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue