mirror of https://github.com/portainer/portainer
140 lines
4.8 KiB
TypeScript
140 lines
4.8 KiB
TypeScript
import { Plus, RotateCw } from 'lucide-react';
|
|
import { FormikErrors } from 'formik';
|
|
|
|
import { KubernetesApplicationPublishingTypes } from '@/kubernetes/models/application/models';
|
|
import { useCurrentUser } from '@/react/hooks/useUser';
|
|
import { useEnvironment } from '@/react/portainer/environments/queries';
|
|
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
|
|
|
import { Card } from '@@/Card';
|
|
import { TextTip } from '@@/Tip/TextTip';
|
|
import { Button } from '@@/buttons';
|
|
import { FormError } from '@@/form-components/FormError';
|
|
import { Link } from '@@/Link';
|
|
|
|
import { generateUniqueName, newPort, serviceFormDefaultValues } from './utils';
|
|
import { ServiceFormValues, ServicePort } from './types';
|
|
import { LoadBalancerServiceForm } from './LoadBalancerServiceForm';
|
|
|
|
interface Props {
|
|
services: ServiceFormValues[];
|
|
onChangeService: (services: ServiceFormValues[]) => void;
|
|
errors?: FormikErrors<ServiceFormValues[]>;
|
|
appName: string;
|
|
selector: Record<string, string>;
|
|
}
|
|
|
|
export function LoadBalancerServicesForm({
|
|
services,
|
|
onChangeService,
|
|
errors,
|
|
appName,
|
|
selector,
|
|
}: Props) {
|
|
const { isAdmin } = useCurrentUser();
|
|
const environmentId = useEnvironmentId();
|
|
const { data: loadBalancerEnabled, ...loadBalancerEnabledQuery } =
|
|
useEnvironment(
|
|
environmentId,
|
|
(environment) => environment?.Kubernetes.Configuration.UseLoadBalancer
|
|
);
|
|
|
|
const loadBalancerServiceCount = services.filter(
|
|
(service) =>
|
|
service.Type === KubernetesApplicationPublishingTypes.LOAD_BALANCER
|
|
).length;
|
|
return (
|
|
<Card className="pb-5">
|
|
<div className="flex flex-col gap-6">
|
|
<TextTip color="blue">
|
|
Allow access to traffic <b>external</b> to the cluster via a{' '}
|
|
<b>LoadBalancer service</b>. If running on a cloud platform, this auto
|
|
provisions a cloud load balancer.
|
|
</TextTip>
|
|
{!loadBalancerEnabled && loadBalancerEnabledQuery.isSuccess && (
|
|
<div className="flex flex-col">
|
|
<FormError>
|
|
{isAdmin ? (
|
|
<>
|
|
Load balancer use is not currently enabled in this cluster.
|
|
Configure via{' '}
|
|
<Link
|
|
to="kubernetes.cluster.setup"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
Cluster Setup
|
|
</Link>{' '}
|
|
and then refresh this tab
|
|
</>
|
|
) : (
|
|
'Load balancer use is not currently enabled in this cluster, contact your administrator.'
|
|
)}
|
|
</FormError>
|
|
<div className="flex">
|
|
<Button
|
|
icon={RotateCw}
|
|
color="default"
|
|
className="!ml-0"
|
|
onClick={() => loadBalancerEnabledQuery.refetch()}
|
|
>
|
|
Refresh
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
{loadBalancerServiceCount > 0 && (
|
|
<div className="flex w-full flex-col gap-4">
|
|
{services.map((service, index) =>
|
|
service.Type ===
|
|
KubernetesApplicationPublishingTypes.LOAD_BALANCER ? (
|
|
<LoadBalancerServiceForm
|
|
key={index}
|
|
serviceName={service.Name}
|
|
servicePorts={service.Ports}
|
|
errors={errors?.[index]?.Ports}
|
|
onChangePort={(servicePorts: ServicePort[]) => {
|
|
const newServices = [...services];
|
|
newServices[index].Ports = servicePorts;
|
|
onChangeService(newServices);
|
|
}}
|
|
services={services}
|
|
serviceIndex={index}
|
|
onChangeService={onChangeService}
|
|
/>
|
|
) : null
|
|
)}
|
|
</div>
|
|
)}
|
|
<div className="flex">
|
|
<Button
|
|
color="secondary"
|
|
className="!ml-0"
|
|
icon={Plus}
|
|
size="small"
|
|
onClick={() => {
|
|
// create a new service form value and add it to the list of services
|
|
const newService = structuredClone(serviceFormDefaultValues);
|
|
newService.Name = generateUniqueName(
|
|
appName,
|
|
services.length + 1,
|
|
services
|
|
);
|
|
newService.Type =
|
|
KubernetesApplicationPublishingTypes.LOAD_BALANCER;
|
|
const newServicePort = newPort(newService.Name);
|
|
newService.Ports = [newServicePort];
|
|
newService.Selector = selector;
|
|
onChangeService([...services, newService]);
|
|
}}
|
|
disabled={!loadBalancerEnabled}
|
|
data-cy="k8sAppCreate-createServiceButton"
|
|
>
|
|
Create service
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|