mirror of https://github.com/portainer/portainer
fix(app): fix app ingress edge cases [EE-5663] (#9150)
Co-authored-by: testa113 <testa113>pull/9181/head
parent
8b11e1678e
commit
c752b98120
|
@ -512,6 +512,17 @@ class KubernetesCreateApplicationController {
|
|||
/* #region SERVICES UI MANAGEMENT */
|
||||
onServicesChange(services) {
|
||||
return this.$async(async () => {
|
||||
// if the ingress isn't found in the currently loaded ingresses, then refresh the ingresses
|
||||
const ingressNamesUsed = services.flatMap((s) => s.Ports.flatMap((p) => (p.ingressPaths ? p.ingressPaths.flatMap((ip) => ip.IngressName || []) : [])));
|
||||
if (ingressNamesUsed.length) {
|
||||
const uniqueIngressNamesUsed = Array.from(new Set(ingressNamesUsed)); // get the unique ingress names used
|
||||
const ingressNamesLoaded = this.ingresses.map((i) => i.Name);
|
||||
const areAllIngressesLoaded = uniqueIngressNamesUsed.every((ingressNameUsed) => ingressNamesLoaded.includes(ingressNameUsed));
|
||||
if (!areAllIngressesLoaded) {
|
||||
this.refreshIngresses();
|
||||
}
|
||||
}
|
||||
// update the services
|
||||
this.formValues.Services = services;
|
||||
});
|
||||
}
|
||||
|
@ -1127,7 +1138,6 @@ class KubernetesCreateApplicationController {
|
|||
this.nodesLabels,
|
||||
this.ingresses
|
||||
);
|
||||
|
||||
this.originalServicePorts = structuredClone(this.formValues.Services.flatMap((service) => service.Ports));
|
||||
this.originalIngressPaths = structuredClone(this.originalServicePorts.flatMap((port) => port.ingressPaths).filter((ingressPath) => ingressPath.Host));
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ export function ClusterIpServiceForm({
|
|||
return (
|
||||
<Card key={portIndex} className="flex flex-col gap-y-3">
|
||||
<div className="flex flex-grow flex-wrap justify-between gap-x-4 gap-y-1">
|
||||
<div className="inline-flex min-w-min flex-grow basis-3/4 flex-wrap gap-2">
|
||||
<div className="inline-flex min-w-min flex-grow basis-3/4 flex-wrap gap-x-4 gap-y-1">
|
||||
<div className="flex min-w-min basis-1/3 flex-col">
|
||||
<ContainerPortInput
|
||||
serviceIndex={serviceIndex}
|
||||
|
|
|
@ -5,8 +5,8 @@ import { InputGroup } from '@@/form-components/InputGroup';
|
|||
type Props = {
|
||||
serviceIndex: number;
|
||||
portIndex: number;
|
||||
value?: number;
|
||||
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||
value?: number;
|
||||
};
|
||||
|
||||
export function ContainerPortInput({
|
||||
|
|
|
@ -63,13 +63,29 @@ export function AppIngressPathForm({
|
|||
isEditMode,
|
||||
]);
|
||||
|
||||
// when the hostname options change (e.g. after a namespace change), update the selected ingress to the first available one
|
||||
// when the hostname options change (e.g. after a namespace change) and the host and ingress is no longer available, update the selected ingress to the first available one
|
||||
useEffect(() => {
|
||||
if (ingressHostOptionsWithCurrentValue) {
|
||||
// some rerenders might not be from a namespace or hostname change so keep the current values if they're still valid
|
||||
const ingressHosts = ingressHostOptionsWithCurrentValue.map(
|
||||
(i) => i.value
|
||||
);
|
||||
const newIngressHostValue = ingressHosts.includes(ingressPath?.Host ?? '')
|
||||
? ingressPath?.Host
|
||||
: ingressHosts[0] ?? '';
|
||||
const ingressNames = ingressHostOptionsWithCurrentValue.map(
|
||||
(i) => i.ingressName
|
||||
);
|
||||
const newIngressNameValue = ingressNames.includes(
|
||||
ingressPath?.IngressName ?? ''
|
||||
)
|
||||
? ingressPath?.IngressName ?? ''
|
||||
: ingressNames[0] ?? '';
|
||||
|
||||
const newIngressPath = {
|
||||
...ingressPath,
|
||||
Host: ingressHostOptionsWithCurrentValue[0]?.value,
|
||||
IngressName: ingressHostOptionsWithCurrentValue[0]?.ingressName,
|
||||
Host: newIngressHostValue,
|
||||
IngressName: newIngressNameValue,
|
||||
};
|
||||
onChangeIngressPath(newIngressPath);
|
||||
setSelectedIngress(ingressHostOptionsWithCurrentValue[0] ?? null);
|
||||
|
@ -78,7 +94,7 @@ export function AppIngressPathForm({
|
|||
}, [ingressHostOptionsWithCurrentValue]);
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-wrap gap-x-4">
|
||||
<div className="flex w-full flex-wrap gap-x-4 gap-y-1">
|
||||
<div className="flex min-w-[250px] basis-1/3 flex-col">
|
||||
<InputGroup size="small">
|
||||
<InputGroup.Addon>Hostname</InputGroup.Addon>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Plus } from 'lucide-react';
|
||||
import { Loader2, Plus } from 'lucide-react';
|
||||
import { FormikErrors } from 'formik';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
|
@ -13,6 +13,7 @@ import { Button } from '@@/buttons';
|
|||
import { SwitchField } from '@@/form-components/SwitchField';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { Link } from '@@/Link';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { ServicePortIngressPath } from '../types';
|
||||
|
||||
|
@ -75,7 +76,12 @@ export function AppIngressPathsForm({
|
|||
}
|
||||
|
||||
if (ingressesQuery.isLoading || ingressControllersQuery.isLoading) {
|
||||
return <p>Loading ingresses...</p>;
|
||||
return (
|
||||
<p className="text-muted mt-2 flex items-center gap-x-2 text-sm">
|
||||
<Icon icon={Loader2} className="animate-spin-slow" />
|
||||
Loading ingresses...
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -154,6 +154,8 @@ export function generateNewIngressesFromFormPaths(
|
|||
return newIngresses;
|
||||
}
|
||||
|
||||
/** prependWithSlash puts a '/' in front of a string if there isn't one there already. If the string is empty, it stays empty */
|
||||
export function prependWithSlash(path?: string) {
|
||||
return path?.startsWith('/') ? path : `/${path}`;
|
||||
if (!path) return '';
|
||||
return path.startsWith('/') ? path : `/${path}`;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue