@@ -6,7 +6,7 @@ import React from 'react';
6
6
import { useTranslation } from 'react-i18next' ;
7
7
import { useDispatch } from 'react-redux' ;
8
8
import { useHistory } from 'react-router-dom' ;
9
- import helpers , { ClusterSettings } from '../../../helpers' ;
9
+ import helpers , { ClusterSettings , DEFAULT_DROP_SHELL_IMAGE } from '../../../helpers' ;
10
10
import { useCluster , useClustersConf } from '../../../lib/k8s' ;
11
11
import { deleteCluster } from '../../../lib/k8s/apiProxy' ;
12
12
import { setConfig } from '../../../redux/configSlice' ;
@@ -47,12 +47,25 @@ function isValidNamespaceFormat(namespace: string) {
47
47
return regex . test ( namespace ) ;
48
48
}
49
49
50
+ function isValidImageFormat ( image : string ) {
51
+ // We allow empty strings just because that's the default value in our case.
52
+ if ( ! image ) {
53
+ return true ;
54
+ }
55
+
56
+ // Validates that the namespace is a valid DNS-1123 label and returns a boolean.
57
+ // https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names
58
+ //const regex = new RegExp('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$');
59
+ return true ;
60
+ }
61
+
50
62
export default function SettingsCluster ( ) {
51
63
const cluster = useCluster ( ) ;
52
64
const clusterConf = useClustersConf ( ) ;
53
65
const { t } = useTranslation ( [ 'translation' ] ) ;
54
66
const [ defaultNamespace , setDefaultNamespace ] = React . useState ( 'default' ) ;
55
67
const [ userDefaultNamespace , setUserDefaultNamespace ] = React . useState ( '' ) ;
68
+ const [ dropShellImage , setDropShellImage ] = React . useState ( '' ) ;
56
69
const [ newAllowedNamespace , setNewAllowedNamespace ] = React . useState ( '' ) ;
57
70
const [ clusterSettings , setClusterSettings ] = React . useState < ClusterSettings | null > ( null ) ;
58
71
const classes = useStyles ( ) ;
@@ -127,10 +140,34 @@ export default function SettingsCluster() {
127
140
} ;
128
141
} , [ userDefaultNamespace ] ) ;
129
142
143
+ React . useEffect ( ( ) => {
144
+ let timeoutHandle : NodeJS . Timeout | null = null ;
145
+
146
+ if ( isEditingDropShellImage ( ) ) {
147
+ // We store the namespace after a timeout.
148
+ timeoutHandle = setTimeout ( ( ) => {
149
+ if ( isValidImageFormat ( dropShellImage ) ) {
150
+ storeNewDropShellImage ( dropShellImage ) ;
151
+ }
152
+ } , 1000 ) ;
153
+ }
154
+
155
+ return ( ) => {
156
+ if ( timeoutHandle ) {
157
+ clearTimeout ( timeoutHandle ) ;
158
+ timeoutHandle = null ;
159
+ }
160
+ } ;
161
+ } , [ dropShellImage ] ) ;
162
+
130
163
function isEditingDefaultNamespace ( ) {
131
164
return clusterSettings ?. defaultNamespace !== userDefaultNamespace ;
132
165
}
133
166
167
+ function isEditingDropShellImage ( ) {
168
+ return clusterSettings ?. dropShellImage !== dropShellImage ;
169
+ }
170
+
134
171
if ( ! cluster ) {
135
172
return null ;
136
173
}
@@ -163,7 +200,18 @@ export default function SettingsCluster() {
163
200
} ) ;
164
201
}
165
202
203
+ function storeNewDropShellImage ( image : string ) {
204
+ setClusterSettings ( ( settings : ClusterSettings | null ) => {
205
+ const newSettings = { ...( settings || { } ) } ;
206
+ if ( isValidImageFormat ( image ) ) {
207
+ newSettings . dropShellImage = image ;
208
+ }
209
+ return newSettings ;
210
+ } ) ;
211
+ }
212
+
166
213
const isValidDefaultNamespace = isValidNamespaceFormat ( userDefaultNamespace ) ;
214
+ const isValidDropShellImage = isValidImageFormat ( dropShellImage ) ;
167
215
const isValidNewAllowedNamespace = isValidNamespaceFormat ( newAllowedNamespace ) ;
168
216
const invalidNamespaceMessage = t (
169
217
"translation|Namespaces must contain only lowercase alphanumeric characters or '-', and must start and end with an alphanumeric character."
@@ -295,6 +343,40 @@ export default function SettingsCluster() {
295
343
</ >
296
344
) ,
297
345
} ,
346
+ {
347
+ name : t ( 'translation|Drop Node Shell Image' ) ,
348
+ value : (
349
+ < TextField
350
+ onChange = { event => {
351
+ let value = event . target . value ;
352
+ value = value . replace ( ' ' , '' ) ;
353
+ setDropShellImage ( value ) ;
354
+ } }
355
+ value = { dropShellImage }
356
+ placeholder = { DEFAULT_DROP_SHELL_IMAGE }
357
+ error = { ! isValidImageFormat }
358
+ helperText = {
359
+ isValidDropShellImage
360
+ ? t (
361
+ 'translation|The default image is used for dropping a shell into a node (when not specified directly).'
362
+ )
363
+ : invalidNamespaceMessage
364
+ }
365
+ InputProps = { {
366
+ endAdornment : isEditingDropShellImage ( ) ? (
367
+ < Icon
368
+ width = { 24 }
369
+ color = { theme . palette . text . secondary }
370
+ icon = "mdi:progress-check"
371
+ />
372
+ ) : (
373
+ < Icon width = { 24 } icon = "mdi:check-bold" />
374
+ ) ,
375
+ className : classes . input ,
376
+ } }
377
+ />
378
+ ) ,
379
+ } ,
298
380
] }
299
381
/>
300
382
</ SectionBox >
0 commit comments