2020-07-14 20:45:19 +00:00
|
|
|
import * as _ from 'lodash-es';
|
2020-08-12 23:30:23 +00:00
|
|
|
import * as JsonPatch from 'fast-json-patch';
|
|
|
|
|
|
|
|
import KubernetesCommonHelper from 'Kubernetes/helpers/commonHelper';
|
|
|
|
import { KubernetesIngressRule, KubernetesIngress } from './models';
|
|
|
|
import { KubernetesIngressCreatePayload, KubernetesIngressRuleCreatePayload, KubernetesIngressRulePathCreatePayload } from './payloads';
|
|
|
|
import { KubernetesIngressClassAnnotation, KubernetesIngressClassMandatoryAnnotations } from './constants';
|
2020-07-14 20:45:19 +00:00
|
|
|
|
|
|
|
export class KubernetesIngressConverter {
|
2020-08-12 23:30:23 +00:00
|
|
|
// TODO: refactor @LP
|
|
|
|
// currently only allows the first non-empty host to be used as the "configured" host.
|
|
|
|
// As we currently only allow a single host to be used for a Portianer-managed ingress
|
|
|
|
// it's working as the only non-empty host will be the one defined by the admin
|
|
|
|
// but it will take a random existing host for non Portainer ingresses (CLI deployed)
|
|
|
|
// Also won't support multiple hosts if we make it available in the future
|
2020-07-14 20:45:19 +00:00
|
|
|
static apiToModel(data) {
|
2020-08-12 23:30:23 +00:00
|
|
|
let host = undefined;
|
|
|
|
const paths = _.flatMap(data.spec.rules, (rule) => {
|
|
|
|
host = host || rule.host; // TODO: refactor @LP - read above
|
|
|
|
return !rule.http
|
|
|
|
? []
|
|
|
|
: _.map(rule.http.paths, (path) => {
|
|
|
|
const ingRule = new KubernetesIngressRule();
|
|
|
|
ingRule.IngressName = data.metadata.name;
|
|
|
|
ingRule.ServiceName = path.backend.serviceName;
|
|
|
|
ingRule.Host = rule.host || '';
|
|
|
|
ingRule.IP = data.status.loadBalancer.ingress ? data.status.loadBalancer.ingress[0].ip : undefined;
|
|
|
|
ingRule.Port = path.backend.servicePort;
|
|
|
|
ingRule.Path = path.path;
|
|
|
|
return ingRule;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
const res = new KubernetesIngress();
|
|
|
|
res.Name = data.metadata.name;
|
|
|
|
res.Namespace = data.metadata.namespace;
|
|
|
|
res.Annotations = data.metadata.annotations || {};
|
|
|
|
res.IngressClassName =
|
|
|
|
data.metadata.annotations && data.metadata.annotations[KubernetesIngressClassAnnotation]
|
|
|
|
? data.metadata.annotations[KubernetesIngressClassAnnotation]
|
|
|
|
: data.spec.ingressClassName;
|
|
|
|
res.Paths = paths;
|
|
|
|
res.Host = host;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static applicationFormValuesToIngresses(formValues, serviceName) {
|
|
|
|
const ingresses = angular.copy(formValues.OriginalIngresses);
|
|
|
|
_.forEach(formValues.PublishedPorts, (p) => {
|
|
|
|
const ingress = _.find(ingresses, { Name: p.IngressName });
|
|
|
|
if (ingress && p.NeedsDeletion) {
|
|
|
|
const path = _.find(ingress.Paths, { Port: p.ContainerPort, ServiceName: serviceName, Path: p.IngressRoute });
|
|
|
|
_.remove(ingress.Paths, path);
|
|
|
|
} else if (ingress && p.IsNew) {
|
|
|
|
const rule = new KubernetesIngressRule();
|
|
|
|
rule.IngressName = ingress.Name;
|
|
|
|
rule.ServiceName = serviceName;
|
|
|
|
rule.Port = p.ContainerPort;
|
|
|
|
rule.Path = _.startsWith(p.IngressRoute, '/') ? p.IngressRoute : '/' + p.IngressRoute;
|
|
|
|
rule.Host = p.IngressHost;
|
|
|
|
ingress.Paths.push(rule);
|
|
|
|
}
|
2020-07-14 20:45:19 +00:00
|
|
|
});
|
2020-08-12 23:30:23 +00:00
|
|
|
return ingresses;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
if (data.Paths && data.Paths.length) {
|
|
|
|
const groups = _.groupBy(data.Paths, 'Host');
|
|
|
|
const rules = _.map(groups, (paths, host) => {
|
|
|
|
const rule = new KubernetesIngressRuleCreatePayload();
|
|
|
|
|
|
|
|
if (host === 'undefined' || _.isEmpty(host)) {
|
|
|
|
host = data.Host;
|
|
|
|
}
|
|
|
|
if (host === data.PreviousHost && host !== data.Host) {
|
|
|
|
host = data.Host;
|
|
|
|
}
|
|
|
|
KubernetesCommonHelper.assignOrDeleteIfEmpty(rule, 'host', host);
|
|
|
|
rule.http.paths = _.map(paths, (p) => {
|
|
|
|
const path = new KubernetesIngressRulePathCreatePayload();
|
|
|
|
path.path = p.Path;
|
|
|
|
path.backend.serviceName = p.ServiceName;
|
|
|
|
path.backend.servicePort = p.Port;
|
|
|
|
return path;
|
|
|
|
});
|
|
|
|
return rule;
|
|
|
|
});
|
|
|
|
KubernetesCommonHelper.assignOrDeleteIfEmpty(res, 'spec.rules', rules);
|
|
|
|
} else if (data.Host) {
|
|
|
|
res.spec.rules = [{ host: data.Host }];
|
|
|
|
} else {
|
|
|
|
delete res.spec.rules;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static patchPayload(oldData, newData) {
|
|
|
|
const oldPayload = KubernetesIngressConverter.createPayload(oldData);
|
|
|
|
const newPayload = KubernetesIngressConverter.createPayload(newData);
|
|
|
|
const payload = JsonPatch.compare(oldPayload, newPayload);
|
|
|
|
return payload;
|
2020-07-14 20:45:19 +00:00
|
|
|
}
|
|
|
|
}
|