feat(ingress): remove ingresses from add and edit application EE-4206 (#7677)

pull/7709/head
Prabhat Khera 2022-09-23 16:11:35 +12:00 committed by GitHub
parent c384d834f5
commit fcb76f570e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 31 additions and 103 deletions

View File

@ -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}`;
});

View File

@ -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(),

View File

@ -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>

View File

@ -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;
});
});

View File

@ -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);

View File

@ -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;
}

View File

@ -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>