mirror of https://github.com/portainer/portainer
feat(ingress): remove ingresses from add and edit application EE-4206 (#7677)
parent
c384d834f5
commit
fcb76f570e
|
@ -93,8 +93,8 @@ angular.module('portainer.docker').controller('KubernetesApplicationsDatatableCo
|
|||
// Map all ingress rules in published ports to their respective URLs
|
||||
const ingressUrls = item.PublishedPorts.flatMap((pp) => pp.IngressRules)
|
||||
.filter(({ Host, IP }) => Host || IP)
|
||||
.map(({ Host, IP, Port, Path }) => {
|
||||
let scheme = Port === 443 ? 'https' : 'http';
|
||||
.map(({ Host, IP, Path, TLS }) => {
|
||||
let scheme = TLS && TLS.filter((tls) => tls.hosts && tls.hosts.includes(Host)).length > 0 ? 'https' : 'http';
|
||||
return `${scheme}://${Host || IP}${Path}`;
|
||||
});
|
||||
|
||||
|
|
|
@ -11,12 +11,7 @@ export default class KubeServicesViewController {
|
|||
|
||||
addEntry(service) {
|
||||
const p = new KubernetesService();
|
||||
if (service === KubernetesApplicationPublishingTypes.INGRESS) {
|
||||
p.Type = KubernetesApplicationPublishingTypes.CLUSTER_IP;
|
||||
p.Ingress = true;
|
||||
} else {
|
||||
p.Type = service;
|
||||
}
|
||||
p.Type = service;
|
||||
|
||||
p.Selector = this.formValues.Selector;
|
||||
|
||||
|
@ -62,8 +57,6 @@ export default class KubeServicesViewController {
|
|||
return KubernetesServiceTypes.NODE_PORT;
|
||||
case KubernetesApplicationPublishingTypes.LOAD_BALANCER:
|
||||
return KubernetesServiceTypes.LOAD_BALANCER;
|
||||
case KubernetesApplicationPublishingTypes.INGRESS:
|
||||
return KubernetesServiceTypes.INGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,8 +72,6 @@ export default class KubeServicesViewController {
|
|||
return 'fa fa-list';
|
||||
case KubernetesApplicationPublishingTypes.LOAD_BALANCER:
|
||||
return 'fa fa-project-diagram';
|
||||
case KubernetesApplicationPublishingTypes.INGRESS:
|
||||
return 'fa fa-route';
|
||||
}
|
||||
}
|
||||
$onInit() {
|
||||
|
@ -98,10 +89,6 @@ export default class KubeServicesViewController {
|
|||
typeName: KubernetesServiceTypes.LOAD_BALANCER,
|
||||
typeValue: KubernetesApplicationPublishingTypes.LOAD_BALANCER,
|
||||
},
|
||||
{
|
||||
typeName: KubernetesServiceTypes.INGRESS,
|
||||
typeValue: KubernetesApplicationPublishingTypes.INGRESS,
|
||||
},
|
||||
],
|
||||
selected: KubernetesApplicationPublishingTypes.CLUSTER_IP,
|
||||
endpointId: this.EndpointProvider.endpointID(),
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<div class="col-sm-12 form-section-title"> Publishing the application </div>
|
||||
|
||||
<div class="col-sm-12 !p-0">
|
||||
<div class="small">
|
||||
<p class="text-muted vertical-center">
|
||||
<pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
|
||||
<span>Publish your application by creating a ClusterIP service for it, which you may then expose via <a target="_blank" ui-sref="kubernetes.ingresses">an ingress</a>.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 form-inline">
|
||||
<div class="col-sm-5" style="padding-left: 0px">
|
||||
|
@ -10,7 +17,18 @@
|
|||
data-cy="k8sAppCreate-publishingModeDropdown"
|
||||
></select>
|
||||
<button type="button" class="btn btn-md btn-default vertical-center !ml-0" ng-click="$ctrl.addEntry( $ctrl.state.selected )" data-cy="k8sAppCreate-createServiceButton">
|
||||
<pr-icon icon="'plus'" size="'sm'" feather="true"></pr-icon> Create service
|
||||
<span
|
||||
class="interactive vertical-center"
|
||||
tooltip-append-to-body="true"
|
||||
tooltip-placement="top"
|
||||
tooltip-class="portainer-tooltip"
|
||||
uib-tooltip="Different service types expose the deployment in alternate ways.
|
||||
ClusterIP exposes it within the cluster (for internal access only).
|
||||
NodePort exposes it (on a high port) across all nodes.
|
||||
LoadBalancer exposes it via an external load balancer."
|
||||
>
|
||||
<pr-icon icon="'plus'" size="'sm'" feather="true"></pr-icon> Create service
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,30 +89,5 @@
|
|||
<pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon> Remove
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div ng-if="$ctrl.formValues.Services[$index].Ingress && $ctrl.formValues.OriginalIngresses.length !== 0">
|
||||
<div class="text-muted">
|
||||
<i class="fa fa-route" aria-hidden="true" style="margin-right: 2px"></i>
|
||||
Ingress
|
||||
</div>
|
||||
<kube-services-item-view
|
||||
original-ingresses="$ctrl.formValues.OriginalIngresses"
|
||||
service-routes="$ctrl.formValues.Services[$index].IngressRoute"
|
||||
ingress-type="$ctrl.formValues.Services[$index].Ingress"
|
||||
service-type="$ctrl.formValues.Services[$index].Type"
|
||||
service-ports="$ctrl.formValues.Services[$index].Ports"
|
||||
service-name="$ctrl.formValues.Services[$index].Name"
|
||||
multi-item-disable="true"
|
||||
></kube-services-item-view>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-dangerlight space-right vertical-center"
|
||||
style="margin-left: 0; margin-top: 10px"
|
||||
ng-click="$ctrl.deleteService( $index )"
|
||||
data-cy="k8sConfigCreate-removeButton"
|
||||
>
|
||||
<pr-icon icon="'trash-2'" size="'md'" feather="true"></pr-icon> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,6 +25,7 @@ export class KubernetesIngressConverter {
|
|||
ingRule.IP = data.status.loadBalancer.ingress ? data.status.loadBalancer.ingress[0].ip : undefined;
|
||||
ingRule.Port = path.backend.service.port.number;
|
||||
ingRule.Path = path.path;
|
||||
ingRule.TLS = data.spec.tls;
|
||||
return ingRule;
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,12 +2,7 @@ import _ from 'lodash-es';
|
|||
import angular from 'angular';
|
||||
import PortainerError from 'Portainer/error';
|
||||
|
||||
import {
|
||||
KubernetesApplication,
|
||||
KubernetesApplicationDeploymentTypes,
|
||||
KubernetesApplicationPublishingTypes,
|
||||
KubernetesApplicationTypes,
|
||||
} from 'Kubernetes/models/application/models';
|
||||
import { KubernetesApplication, KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
|
||||
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
|
||||
import KubernetesApplicationRollbackHelper from 'Kubernetes/helpers/application/rollback';
|
||||
import KubernetesApplicationConverter from 'Kubernetes/converters/application';
|
||||
|
@ -17,7 +12,6 @@ import { KubernetesDaemonSet } from 'Kubernetes/models/daemon-set/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';
|
||||
import KubernetesPodConverter from 'Kubernetes/pod/converter';
|
||||
|
||||
class KubernetesApplicationService {
|
||||
|
@ -79,12 +73,6 @@ class KubernetesApplicationService {
|
|||
return apiService;
|
||||
}
|
||||
|
||||
_generateIngressPatchPromises(oldIngresses, newIngresses) {
|
||||
return _.map(newIngresses, (newIng) => {
|
||||
const oldIng = _.find(oldIngresses, { Name: newIng.Name });
|
||||
return this.KubernetesIngressService.patch(oldIng, newIng);
|
||||
});
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
/* #region GET */
|
||||
|
@ -221,26 +209,12 @@ class KubernetesApplicationService {
|
|||
*/
|
||||
async createAsync(formValues) {
|
||||
// formValues -> Application
|
||||
let [app, headlessService, services, service, claims] = KubernetesApplicationConverter.applicationFormValuesToApplication(formValues);
|
||||
let [app, headlessService, services, claims] = KubernetesApplicationConverter.applicationFormValuesToApplication(formValues);
|
||||
|
||||
if (services) {
|
||||
services.forEach(async (service) => {
|
||||
await this.KubernetesServiceService.create(service);
|
||||
});
|
||||
|
||||
//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));
|
||||
}
|
||||
}
|
||||
|
||||
if (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);
|
||||
|
@ -322,20 +296,10 @@ class KubernetesApplicationService {
|
|||
newServices.forEach(async (service) => {
|
||||
await this.KubernetesServiceService.create(service);
|
||||
});
|
||||
|
||||
// Create multiple ingress
|
||||
const ingresses = KubernetesIngressConverter.generateNewIngresses(oldFormValues.OriginalIngresses, newServices);
|
||||
if (ingresses) {
|
||||
await Promise.all(this._generateIngressPatchPromises(oldFormValues.OriginalIngresses, ingresses));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete services ( only called when all services been deleted )
|
||||
if (oldServices.length !== 0 && newServices.length === 0) {
|
||||
const ingresses = KubernetesIngressConverter.removeIngressesPaths(oldFormValues.OriginalIngresses, oldServices);
|
||||
if (ingresses) {
|
||||
await Promise.all(this._generateIngressPatchPromises(oldFormValues.OriginalIngresses, ingresses));
|
||||
}
|
||||
await this.KubernetesServiceService.deleteAll(oldServices);
|
||||
}
|
||||
|
||||
|
@ -356,15 +320,6 @@ class KubernetesApplicationService {
|
|||
await this.KubernetesServiceService.create(newService);
|
||||
}
|
||||
});
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
const newKind = KubernetesHorizontalPodAutoScalerHelper.getApplicationTypeString(newApp);
|
||||
|
@ -438,17 +393,6 @@ class KubernetesApplicationService {
|
|||
if (application.ServiceType) {
|
||||
// delete headless service && non-headless service
|
||||
await this.KubernetesServiceService.delete(application.Services);
|
||||
|
||||
if (application.Ingresses.length) {
|
||||
const originalIngresses = await this.KubernetesIngressService.get(payload.Namespace);
|
||||
const formValues = {
|
||||
OriginalIngresses: originalIngresses,
|
||||
PublishedPorts: KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(application.ServiceType, application.PublishedPorts),
|
||||
};
|
||||
const ingresses = KubernetesIngressConverter.applicationFormValuesToDeleteIngresses(formValues, application);
|
||||
|
||||
await Promise.all(this._generateIngressPatchPromises(formValues.OriginalIngresses, ingresses));
|
||||
}
|
||||
}
|
||||
if (!_.isEmpty(application.AutoScaler)) {
|
||||
await this.KubernetesHorizontalPodAutoScalerService.delete(application.AutoScaler);
|
||||
|
|
|
@ -18,6 +18,7 @@ export default class KubernetesApplicationIngressController {
|
|||
_.forEach(ingresses, (ingress) => {
|
||||
_.forEach(ingress.Paths, (path) => {
|
||||
if (path.ServiceName === service.metadata.name) {
|
||||
path.Secure = ingress.TLS && ingress.TLS.filter((tls) => tls.hosts && tls.hosts.includes(path.Host)).length > 0;
|
||||
this.applicationIngress.push(path);
|
||||
this.hasIngress = true;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,15 @@
|
|||
<td style="width: 15%">HTTP Route</td>
|
||||
</tr>
|
||||
<tr ng-repeat="ingress in $ctrl.applicationIngress">
|
||||
<td>{{ ingress.IngressName }}</td>
|
||||
<td
|
||||
><a ui-sref="kubernetes.ingresses.edit({ name: ingress.IngressName, namespace: $ctrl.application.ResourcePool })">{{ ingress.IngressName }}</a></td
|
||||
>
|
||||
<td>{{ ingress.ServiceName }}</td>
|
||||
<td>{{ ingress.Host }}</td>
|
||||
<td>{{ ingress.Port }}</td>
|
||||
<td>{{ ingress.Path }}</td>
|
||||
<td
|
||||
><a target="_blank" href="http://{{ ingress.Host }}{{ ingress.Path }}">{{ ingress.Host }}{{ ingress.Path }}</a></td
|
||||
><a target="_blank" href="{{ ingress.Secure ? 'https' : 'http' }}://{{ ingress.Host }}{{ ingress.Path }}">{{ ingress.Host }}{{ ingress.Path }}</a></td
|
||||
>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
|
Loading…
Reference in New Issue