2023-08-01 07:31:35 +00:00
import { ChangeEvent , ReactNode , useEffect } from 'react' ;
2023-07-06 21:37:04 +00:00
import { Plus , RefreshCw , Trash2 } from 'lucide-react' ;
2022-11-28 02:00:28 +00:00
import Route from '@/assets/ico/route.svg?c' ;
2022-09-21 04:49:42 +00:00
import { Link } from '@@/Link' ;
2023-08-01 07:31:35 +00:00
import { Option } from '@@/form-components/Input/Select' ;
2022-09-21 04:49:42 +00:00
import { FormError } from '@@/form-components/FormError' ;
import { Widget , WidgetBody , WidgetTitle } from '@@/Widget' ;
import { Tooltip } from '@@/Tip/Tooltip' ;
import { Button } from '@@/buttons' ;
2023-03-01 00:11:12 +00:00
import { TooltipWithChildren } from '@@/Tip/TooltipWithChildren' ;
2023-06-01 20:35:15 +00:00
import { TextTip } from '@@/Tip/TextTip' ;
2023-08-01 07:31:35 +00:00
import { Card } from '@@/Card' ;
import { InputGroup } from '@@/form-components/InputGroup' ;
import { InlineLoader } from '@@/InlineLoader' ;
import { Select } from '@@/form-components/ReactSelect' ;
2022-09-21 04:49:42 +00:00
2022-11-07 06:03:11 +00:00
import { Annotations } from './Annotations' ;
2023-08-01 07:31:35 +00:00
import { GroupedServiceOptions , Rule , ServicePorts } from './types' ;
2022-09-21 04:49:42 +00:00
import '../style.css' ;
const PathTypes : Record < string , string [ ] > = {
nginx : [ 'ImplementationSpecific' , 'Prefix' , 'Exact' ] ,
traefik : [ 'Prefix' , 'Exact' ] ,
other : [ 'Prefix' , 'Exact' ] ,
} ;
const PlaceholderAnnotations : Record < string , string [ ] > = {
nginx : [ 'e.g. nginx.ingress.kubernetes.io/rewrite-target' , '/$1' ] ,
traefik : [ 'e.g. traefik.ingress.kubernetes.io/router.tls' , 'true' ] ,
other : [ 'e.g. app.kubernetes.io/name' , 'examplename' ] ,
} ;
interface Props {
environmentID : number ;
rule : Rule ;
errors : Record < string , ReactNode > ;
isEdit : boolean ;
namespace : string ;
servicePorts : ServicePorts ;
ingressClassOptions : Option < string > [ ] ;
2023-08-01 07:31:35 +00:00
isIngressClassOptionsLoading : boolean ;
serviceOptions : GroupedServiceOptions ;
2022-09-21 04:49:42 +00:00
tlsOptions : Option < string > [ ] ;
namespacesOptions : Option < string > [ ] ;
2023-08-01 07:31:35 +00:00
isNamespaceOptionsLoading : boolean ;
2022-09-21 04:49:42 +00:00
removeIngressRoute : ( hostIndex : number , pathIndex : number ) = > void ;
removeIngressHost : ( hostIndex : number ) = > void ;
removeAnnotation : ( index : number ) = > void ;
addNewIngressHost : ( noHost? : boolean ) = > void ;
addNewIngressRoute : ( hostIndex : number ) = > void ;
2022-10-24 20:41:30 +00:00
addNewAnnotation : ( type ? : 'rewrite' | 'regex' | 'ingressClass' ) = > void ;
2022-09-21 04:49:42 +00:00
handleNamespaceChange : ( val : string ) = > void ;
handleHostChange : ( hostIndex : number , val : string ) = > void ;
handleTLSChange : ( hostIndex : number , tls : string ) = > void ;
handleIngressChange : (
key : 'IngressName' | 'IngressClassName' ,
value : string
) = > void ;
handleAnnotationChange : (
index : number ,
key : 'Key' | 'Value' ,
val : string
) = > void ;
handlePathChange : (
hostIndex : number ,
pathIndex : number ,
key : 'Route' | 'PathType' | 'ServiceName' | 'ServicePort' ,
val : string
) = > void ;
reloadTLSCerts : ( ) = > void ;
}
export function IngressForm ( {
environmentID ,
rule ,
isEdit ,
servicePorts ,
tlsOptions ,
handleTLSChange ,
addNewIngressHost ,
serviceOptions ,
handleHostChange ,
handleIngressChange ,
handlePathChange ,
addNewIngressRoute ,
removeIngressRoute ,
removeIngressHost ,
addNewAnnotation ,
removeAnnotation ,
reloadTLSCerts ,
handleAnnotationChange ,
ingressClassOptions ,
2023-08-01 07:31:35 +00:00
isIngressClassOptionsLoading ,
2022-09-21 04:49:42 +00:00
errors ,
namespacesOptions ,
2023-08-01 07:31:35 +00:00
isNamespaceOptionsLoading ,
2022-09-21 04:49:42 +00:00
handleNamespaceChange ,
namespace ,
} : Props ) {
const hasNoHostRule = rule . Hosts ? . some ( ( host ) = > host . NoHost ) ;
const placeholderAnnotation =
2022-10-05 02:17:53 +00:00
PlaceholderAnnotations [ rule . IngressType || 'other' ] ||
PlaceholderAnnotations . other ;
const pathTypes = PathTypes [ rule . IngressType || 'other' ] || PathTypes . other ;
2022-09-21 04:49:42 +00:00
2023-08-01 07:31:35 +00:00
// when the namespace options update the value to an available one
useEffect ( ( ) = > {
const namespaces = namespacesOptions . map ( ( option ) = > option . value ) ;
if ( ! namespaces . includes ( namespace ) && namespaces . length > 0 ) {
handleNamespaceChange ( namespaces [ 0 ] ) ;
}
} , [ namespacesOptions , namespace , handleNamespaceChange ] ) ;
// when the ingress class options update update the value to an available one
useEffect ( ( ) = > {
const ingressClasses = ingressClassOptions . map ( ( option ) = > option . value ) ;
if (
! ingressClasses . includes ( rule . IngressClassName ) &&
ingressClasses . length > 0
) {
handleIngressChange ( 'IngressClassName' , ingressClasses [ 0 ] ) ;
}
} , [ ingressClassOptions , rule . IngressClassName , handleIngressChange ] ) ;
2022-09-21 04:49:42 +00:00
return (
< Widget >
2022-11-28 02:00:28 +00:00
< WidgetTitle icon = { Route } title = "Ingress" / >
2022-09-21 04:49:42 +00:00
< WidgetBody key = { rule . Key + rule . Namespace } >
< div className = "row" >
< div className = "form-horizontal" >
< div className = "form-group" >
< label
2023-03-07 08:09:41 +00:00
className = "control-label text-muted col-sm-3 col-lg-2"
2022-09-21 04:49:42 +00:00
htmlFor = "namespace"
>
Namespace
< / label >
2023-08-01 07:31:35 +00:00
{ isNamespaceOptionsLoading && (
< div className = "col-sm-4" >
< InlineLoader className = "pt-2" >
Loading namespaces . . .
< / InlineLoader >
< / div >
) }
{ ! isNamespaceOptionsLoading && (
< div className = { ` col-sm-4 ${ isEdit && 'control-label' } ` } >
{ isEdit ? (
namespace
) : (
< Select
name = "namespaces"
options = { namespacesOptions || [ ] }
value = { { value : namespace , label : namespace } }
isDisabled = { isEdit }
onChange = { ( val ) = >
handleNamespaceChange ( val ? . value || '' )
}
/ >
) }
< / div >
) }
2022-09-21 04:49:42 +00:00
< / div >
< / div >
< / div >
{ namespace && (
< div className = "row" >
< div className = "form-horizontal" >
< div className = "form-group" >
< label
className = "control-label text-muted col-sm-3 col-lg-2 required"
htmlFor = "ingress_name"
>
Ingress name
< / label >
< div className = "col-sm-4" >
{ isEdit ? (
rule . IngressName
) : (
< input
name = "ingress_name"
type = "text"
className = "form-control"
placeholder = "Ingress name"
defaultValue = { rule . IngressName }
onChange = { ( e : ChangeEvent < HTMLInputElement > ) = >
handleIngressChange ( 'IngressName' , e . target . value )
}
disabled = { isEdit }
/ >
) }
{ errors . ingressName && ! isEdit && (
2023-02-12 21:04:24 +00:00
< FormError className = "error-inline mt-1" >
2022-09-21 04:49:42 +00:00
{ errors . ingressName }
< / FormError >
) }
< / div >
< / div >
2022-10-05 02:17:53 +00:00
< div className = "form-group" key = { ingressClassOptions . toString ( ) } >
2022-09-21 04:49:42 +00:00
< label
className = "control-label text-muted col-sm-3 col-lg-2 required"
htmlFor = "ingress_class"
>
Ingress class
< / label >
< div className = "col-sm-4" >
2023-08-01 07:31:35 +00:00
{ isIngressClassOptionsLoading && (
< InlineLoader className = "pt-2" >
Loading ingress classes . . .
< / InlineLoader >
) }
{ ! isIngressClassOptionsLoading && (
< >
< Select
name = "ingress_class"
placeholder = "Ingress name"
options = { ingressClassOptions }
value = { {
label : rule.IngressClassName ,
value : rule.IngressClassName ,
} }
onChange = { ( ingressClassOption ) = >
handleIngressChange (
'IngressClassName' ,
ingressClassOption ? . value || ''
)
}
/ >
{ errors . className && (
< FormError className = "error-inline mt-1" >
{ errors . className }
< / FormError >
) }
< / >
) }
2022-09-21 04:49:42 +00:00
{ errors . className && (
2023-02-12 21:04:24 +00:00
< FormError className = "error-inline mt-1" >
2022-09-21 04:49:42 +00:00
{ errors . className }
< / FormError >
) }
< / div >
< / div >
< / div >
2023-02-12 21:04:24 +00:00
< div className = "col-sm-12 text-muted !mb-0 px-0" >
2023-03-01 00:11:12 +00:00
< div className = "control-label !mb-3 text-left font-medium" >
Annotations
< Tooltip
message = {
< div className = "vertical-center" >
< span >
2023-03-13 21:41:35 +00:00
Allows specifying of { ' ' }
2023-03-01 00:11:12 +00:00
< a
href = "https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/"
target = "_black"
>
annotations
< / a > { ' ' }
for the object . See further Kubernetes documentation on { ' ' }
< a
href = "https://kubernetes.io/docs/reference/labels-annotations-taints/"
target = "_black"
>
well - known annotations
< / a >
.
< / span >
< / div >
}
/ >
< / div >
2022-09-21 04:49:42 +00:00
< / div >
{ rule ? . Annotations && (
< Annotations
placeholder = { placeholderAnnotation }
annotations = { rule . Annotations }
handleAnnotationChange = { handleAnnotationChange }
removeAnnotation = { removeAnnotation }
errors = { errors }
/ >
) }
2023-02-12 21:04:24 +00:00
< div className = "col-sm-12 anntation-actions p-0" >
2023-03-01 00:11:12 +00:00
< TooltipWithChildren message = "Use annotations to configure options for an ingress. Review Nginx or Traefik documentation to find the annotations supported by your choice of ingress type." >
< span >
2022-09-21 04:49:42 +00:00
< Button
2023-03-01 00:11:12 +00:00
className = "btn btn-sm btn-light mb-2 !ml-0"
onClick = { ( ) = > addNewAnnotation ( ) }
2022-09-21 04:49:42 +00:00
icon = { Plus }
>
{ ' ' }
2023-03-01 00:11:12 +00:00
Add annotation
2022-09-21 04:49:42 +00:00
< / Button >
2023-03-01 00:11:12 +00:00
< / span >
< / TooltipWithChildren >
2022-09-21 04:49:42 +00:00
2023-03-01 00:11:12 +00:00
{ rule . IngressType === 'nginx' && (
< >
< TooltipWithChildren message = "When the exposed URLs for your applications differ from the specified paths in the ingress, use the rewrite target annotation to denote the path to redirect to." >
< span >
< Button
className = "btn btn-sm btn-light mb-2 ml-2"
onClick = { ( ) = > addNewAnnotation ( 'rewrite' ) }
icon = { Plus }
data - cy = "add-rewrite-annotation"
>
Add rewrite annotation
< / Button >
< / span >
< / TooltipWithChildren >
< TooltipWithChildren message = "Enable use of regular expressions in ingress paths (set in the ingress details of an application). Use this along with rewrite-target to specify the regex capturing group to be replaced, e.g. path regex of ^/foo/(,*) and rewrite-target of /bar/$1 rewrites example.com/foo/account to example.com/bar/account." >
< span >
< Button
className = "btn btn-sm btn-light mb-2 ml-2"
onClick = { ( ) = > addNewAnnotation ( 'regex' ) }
icon = { Plus }
data - cy = "add-regex-annotation"
>
Add regular expression annotation
< / Button >
< / span >
< / TooltipWithChildren >
2022-09-21 04:49:42 +00:00
< / >
) }
2022-10-24 20:41:30 +00:00
{ rule . IngressType === 'custom' && (
< Button
className = "btn btn-sm btn-light mb-2 ml-2"
onClick = { ( ) = > addNewAnnotation ( 'ingressClass' ) }
icon = { Plus }
data - cy = "add-ingress-class-annotation"
>
Add kubernetes . io / ingress . class annotation
< / Button >
) }
2022-09-21 04:49:42 +00:00
< / div >
2023-02-12 21:04:24 +00:00
< div className = "col-sm-12 text-muted px-0" > Rules < / div >
2022-09-21 04:49:42 +00:00
< / div >
) }
{ namespace &&
rule ? . Hosts ? . map ( ( host , hostIndex ) = > (
2023-08-01 07:31:35 +00:00
< Card key = { host . Key } className = "mb-5" >
< div className = "flex flex-col" >
< div className = "row rule-actions" >
2022-09-21 04:49:42 +00:00
< div className = "col-sm-3 p-0" >
{ ! host . NoHost ? 'Rule' : 'Fallback rule' }
< / div >
< div className = "col-sm-9 p-0 text-right" >
< Button
2022-10-07 03:55:11 +00:00
className = "btn btn-sm ml-2"
color = "dangerlight"
2022-09-21 04:49:42 +00:00
type = "button"
data - cy = { ` k8sAppCreate-rmHostButton_ ${ hostIndex } ` }
onClick = { ( ) = > removeIngressHost ( hostIndex ) }
disabled = { rule . Hosts . length === 1 }
icon = { Trash2 }
>
Remove rule
< / Button >
< / div >
< / div >
{ ! host . NoHost && (
< div className = "row" >
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-6 col-lg-4 !pl-0 !pr-2" >
2023-08-01 07:31:35 +00:00
< InputGroup size = "small" >
< InputGroup.Addon required > Hostname < / InputGroup.Addon >
< InputGroup.Input
2022-09-21 04:49:42 +00:00
name = { ` ingress_host_ ${ hostIndex } ` }
type = "text"
className = "form-control form-control-sm"
placeholder = "e.g. example.com"
defaultValue = { host . Host }
onChange = { ( e : ChangeEvent < HTMLInputElement > ) = >
handleHostChange ( hostIndex , e . target . value )
}
/ >
2023-08-01 07:31:35 +00:00
< / InputGroup >
2022-09-21 04:49:42 +00:00
{ errors [ ` hosts[ ${ hostIndex } ].host ` ] && (
< FormError className = "mt-1 !mb-0" >
{ errors [ ` hosts[ ${ hostIndex } ].host ` ] }
< / FormError >
) }
< / div >
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-6 col-lg-4 !pr-0 !pl-2" >
2023-08-01 07:31:35 +00:00
< InputGroup size = "small" >
< InputGroup.Addon > TLS secret < / InputGroup.Addon >
2022-09-21 04:49:42 +00:00
< Select
key = { tlsOptions . toString ( ) + host . Secret }
name = { ` ingress_tls_ ${ hostIndex } ` }
2023-08-01 07:31:35 +00:00
value = { {
value : rule.Hosts [ hostIndex ] . Secret ,
label : rule.Hosts [ hostIndex ] . Secret || 'No TLS' ,
} }
onChange = { ( TLSOption ) = >
handleTLSChange ( hostIndex , TLSOption ? . value || '' )
2022-09-21 04:49:42 +00:00
}
2023-08-01 07:31:35 +00:00
size = "sm"
2022-09-21 04:49:42 +00:00
/ >
2023-06-13 00:38:00 +00:00
{ ! host . NoHost && (
< div className = "input-group-btn" >
< Button
className = "btn btn-light btn-sm !ml-0 !rounded-l-none"
onClick = { ( ) = > reloadTLSCerts ( ) }
icon = { RefreshCw }
/ >
< / div >
) }
2023-08-01 07:31:35 +00:00
< / InputGroup >
2022-09-21 04:49:42 +00:00
< / div >
2023-06-13 00:38:00 +00:00
< div className = "col-sm-12 col-lg-4 flex h-[30px] items-center pl-2" >
2023-06-01 20:35:15 +00:00
< TextTip color = "blue" >
2023-06-11 21:46:48 +00:00
You may also use the { ' ' }
2022-09-21 04:49:42 +00:00
< Link
2023-06-11 21:46:48 +00:00
to = "kubernetes.secrets.new"
2022-09-21 04:49:42 +00:00
params = { { id : environmentID } }
className = "text-primary"
target = "_blank"
>
2023-06-11 21:46:48 +00:00
Create secret
< / Link > { ' ' }
function , and reload the dropdown .
2023-06-01 20:35:15 +00:00
< / TextTip >
< / div >
2022-09-21 04:49:42 +00:00
< / div >
) }
{ host . NoHost && (
2023-07-06 21:37:04 +00:00
< TextTip color = "blue" >
A fallback rule has no host specified . This rule only
applies when an inbound request has a hostname that does not
match with any of your other rules .
< / TextTip >
2022-09-21 04:49:42 +00:00
) }
< div className = "row" >
2023-02-12 21:04:24 +00:00
< div className = "col-sm-12 text-muted !mb-0 mt-2 px-0" >
2022-09-21 04:49:42 +00:00
Paths
< / div >
< / div >
2023-06-13 00:38:00 +00:00
2023-06-14 01:45:25 +00:00
{ ! host . Paths . length && (
2023-07-06 21:37:04 +00:00
< TextTip className = "mt-2" color = "blue" >
2023-06-14 01:45:25 +00:00
You may save the ingress without a path and it will then be
an < b > ingress default < / b > that a user may select via the
hostname dropdown in Create / Edit application .
2023-07-06 21:37:04 +00:00
< / TextTip >
2023-06-14 01:45:25 +00:00
) }
2023-06-13 00:38:00 +00:00
2022-09-21 04:49:42 +00:00
{ host . Paths . map ( ( path , pathIndex ) = > (
< div
2023-02-12 21:04:24 +00:00
className = "row path mt-5 !mb-5"
2022-09-21 04:49:42 +00:00
key = { ` path_ ${ path . Key } } ` }
>
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-3 col-xl-2 !m-0 !pl-0" >
2023-08-01 07:31:35 +00:00
< InputGroup size = "small" >
< InputGroup.Addon required > Service < / InputGroup.Addon >
2022-09-21 04:49:42 +00:00
< Select
key = { serviceOptions . toString ( ) + path . ServiceName }
name = { ` ingress_service_ ${ hostIndex } _ ${ pathIndex } ` }
options = { serviceOptions }
2023-08-01 07:31:35 +00:00
value = { {
value : path.ServiceName ,
label : path.ServiceName || 'Select a service' ,
} }
onChange = { ( serviceOption ) = >
2022-09-21 04:49:42 +00:00
handlePathChange (
hostIndex ,
pathIndex ,
'ServiceName' ,
2023-08-01 07:31:35 +00:00
serviceOption ? . value || ''
2022-09-21 04:49:42 +00:00
)
}
2023-08-01 07:31:35 +00:00
size = "sm"
2022-09-21 04:49:42 +00:00
/ >
2023-08-01 07:31:35 +00:00
< / InputGroup >
2022-09-21 04:49:42 +00:00
{ errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].servicename `
] && (
2023-02-12 21:04:24 +00:00
< FormError className = "error-inline mt-1 !mb-0" >
2022-09-21 04:49:42 +00:00
{
errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].servicename `
]
}
< / FormError >
) }
< / div >
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-2 col-xl-2 !m-0 !pl-0" >
2022-09-21 04:49:42 +00:00
{ servicePorts && (
< >
2023-08-01 07:31:35 +00:00
< InputGroup size = "small" >
< InputGroup.Addon required >
2022-09-21 04:49:42 +00:00
Service port
2023-08-01 07:31:35 +00:00
< / InputGroup.Addon >
2022-09-21 04:49:42 +00:00
< Select
key = { servicePorts . toString ( ) + path . ServicePort }
name = { ` ingress_servicePort_ ${ hostIndex } _ ${ pathIndex } ` }
options = {
2023-08-01 07:31:35 +00:00
servicePorts [ path . ServiceName ] ? . map (
( portOption ) = > ( {
. . . portOption ,
value : portOption.value.toString ( ) ,
} )
) || [ ]
2022-09-21 04:49:42 +00:00
}
2023-08-01 07:31:35 +00:00
onChange = { ( option ) = >
2022-09-21 04:49:42 +00:00
handlePathChange (
hostIndex ,
pathIndex ,
'ServicePort' ,
2023-08-01 07:31:35 +00:00
option ? . value || ''
2022-09-21 04:49:42 +00:00
)
}
2023-08-01 07:31:35 +00:00
value = { {
label : (
path . ServicePort || 'Select a port'
) . toString ( ) ,
value :
rule . Hosts [ hostIndex ] . Paths [
pathIndex
] . ServicePort . toString ( ) ,
} }
size = "sm"
2022-09-21 04:49:42 +00:00
/ >
2023-08-01 07:31:35 +00:00
< / InputGroup >
2022-09-21 04:49:42 +00:00
{ errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].serviceport `
] && (
< FormError className = "mt-1 !mb-0" >
{
errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].serviceport `
]
}
< / FormError >
) }
< / >
) }
< / div >
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-3 col-xl-2 !m-0 !pl-0" >
2023-08-01 07:31:35 +00:00
< InputGroup size = "small" >
< InputGroup.Addon > Path type < / InputGroup.Addon >
< Select < Option < string > >
2022-09-21 04:49:42 +00:00
key = { servicePorts . toString ( ) + path . PathType }
name = { ` ingress_pathType_ ${ hostIndex } _ ${ pathIndex } ` }
options = {
2023-08-01 07:31:35 +00:00
pathTypes ? . map ( ( type ) = > ( {
label : type ,
value : type ,
} ) ) || [ ]
2022-09-21 04:49:42 +00:00
}
2023-08-01 07:31:35 +00:00
onChange = { ( option ) = >
2022-09-21 04:49:42 +00:00
handlePathChange (
hostIndex ,
pathIndex ,
'PathType' ,
2023-08-01 07:31:35 +00:00
option ? . value || ''
2022-09-21 04:49:42 +00:00
)
}
2023-08-01 07:31:35 +00:00
value = { {
label : path.PathType || 'Select a path type' ,
value : path.PathType || '' ,
} }
size = "sm"
2022-09-21 04:49:42 +00:00
/ >
2023-08-01 07:31:35 +00:00
< / InputGroup >
2022-09-21 04:49:42 +00:00
{ errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].pathType `
] && (
< FormError className = "mt-1 !mb-0" >
{
errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].pathType `
]
}
< / FormError >
) }
< / div >
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-3 col-xl-3 !m-0 !pl-0" >
2023-08-01 07:31:35 +00:00
< InputGroup size = "small" >
< InputGroup.Addon required > Path < / InputGroup.Addon >
< InputGroup.Input
2022-09-21 04:49:42 +00:00
className = "form-control"
name = { ` ingress_route_ ${ hostIndex } - ${ pathIndex } ` }
placeholder = "/example"
data - pattern = "/^(\/?[a-zA-Z0-9]+([a-zA-Z0-9-/_]*[a-zA-Z0-9])?|[a-zA-Z0-9]+)|(\/){1}$/"
data - cy = { ` k8sAppCreate-route_ ${ hostIndex } - ${ pathIndex } ` }
defaultValue = { path . Route }
onChange = { ( e : ChangeEvent < HTMLInputElement > ) = >
handlePathChange (
hostIndex ,
pathIndex ,
'Route' ,
e . target . value
)
}
/ >
2023-08-01 07:31:35 +00:00
< / InputGroup >
2022-09-21 04:49:42 +00:00
{ errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].path `
] && (
< FormError className = "mt-1 !mb-0" >
{
errors [
` hosts[ ${ hostIndex } ].paths[ ${ pathIndex } ].path `
]
}
< / FormError >
) }
< / div >
2023-02-12 21:04:24 +00:00
< div className = "form-group col-sm-1 !m-0 !pl-0" >
2022-09-21 04:49:42 +00:00
< Button
2023-06-13 00:38:00 +00:00
className = "btn-only-icon vertical-center !ml-0"
2022-10-07 03:55:11 +00:00
color = "dangerlight"
2022-09-21 04:49:42 +00:00
type = "button"
data - cy = { ` k8sAppCreate-rmPortButton_ ${ hostIndex } - ${ pathIndex } ` }
onClick = { ( ) = > removeIngressRoute ( hostIndex , pathIndex ) }
icon = { Trash2 }
2023-06-13 00:38:00 +00:00
size = "small"
2023-06-14 01:45:25 +00:00
disabled = { host . Paths . length === 1 && host . NoHost }
2022-09-21 04:49:42 +00:00
/ >
< / div >
< / div >
) ) }
< div className = "row mt-5" >
< Button
2023-06-13 00:38:00 +00:00
className = "!ml-0"
2022-09-21 04:49:42 +00:00
type = "button"
onClick = { ( ) = > addNewIngressRoute ( hostIndex ) }
icon = { Plus }
2023-06-13 00:38:00 +00:00
size = "small"
color = "default"
2022-09-21 04:49:42 +00:00
>
Add path
< / Button >
< / div >
< / div >
2023-08-01 07:31:35 +00:00
< / Card >
2022-09-21 04:49:42 +00:00
) ) }
{ namespace && (
2023-02-12 21:04:24 +00:00
< div className = "row rules-action p-0" >
< div className = "col-sm-12 vertical-center p-0" >
2022-09-21 04:49:42 +00:00
< Button
2023-06-13 00:38:00 +00:00
className = "!ml-0"
2022-09-21 04:49:42 +00:00
type = "button"
onClick = { ( ) = > addNewIngressHost ( ) }
icon = { Plus }
2023-06-13 00:38:00 +00:00
color = "default"
size = "small"
2022-09-21 04:49:42 +00:00
>
Add new host
< / Button >
< Button
2023-06-13 00:38:00 +00:00
className = "ml-2"
2022-09-21 04:49:42 +00:00
type = "button"
onClick = { ( ) = > addNewIngressHost ( true ) }
disabled = { hasNoHostRule }
icon = { Plus }
2023-06-13 00:38:00 +00:00
color = "default"
size = "small"
2022-09-21 04:49:42 +00:00
>
Add fallback rule
< / Button >
< Tooltip message = "A fallback rule will be applied to all requests that do not match any of the defined hosts." / >
< / div >
< / div >
) }
< / WidgetBody >
< / Widget >
) ;
}