Skip to content

Commit

Permalink
frontend: add node shell
Browse files Browse the repository at this point in the history
Fixes #996

Signed-off-by: farodin91 <[email protected]>
  • Loading branch information
farodin91 committed Jan 14, 2024
1 parent aeb60f0 commit c171dd8
Show file tree
Hide file tree
Showing 18 changed files with 448 additions and 143 deletions.
84 changes: 83 additions & 1 deletion frontend/src/components/App/Settings/SettingsCluster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
import helpers, { ClusterSettings } from '../../../helpers';
import helpers, { ClusterSettings, DEFAULT_DROP_SHELL_IMAGE } from '../../../helpers';
import { useCluster, useClustersConf } from '../../../lib/k8s';
import { deleteCluster } from '../../../lib/k8s/apiProxy';
import { setConfig } from '../../../redux/configSlice';
Expand Down Expand Up @@ -47,12 +47,25 @@ function isValidNamespaceFormat(namespace: string) {
return regex.test(namespace);
}

function isValidImageFormat(image: string) {
// We allow empty strings just because that's the default value in our case.
if (!image) {
return true;
}

// Validates that the namespace is a valid DNS-1123 label and returns a boolean.
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names
//const regex = new RegExp('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$');
return true;
}

export default function SettingsCluster() {
const cluster = useCluster();
const clusterConf = useClustersConf();
const { t } = useTranslation(['translation']);
const [defaultNamespace, setDefaultNamespace] = React.useState('default');
const [userDefaultNamespace, setUserDefaultNamespace] = React.useState('');
const [dropShellImage, setDropShellImage] = React.useState('');
const [newAllowedNamespace, setNewAllowedNamespace] = React.useState('');
const [clusterSettings, setClusterSettings] = React.useState<ClusterSettings | null>(null);
const classes = useStyles();
Expand Down Expand Up @@ -127,10 +140,34 @@ export default function SettingsCluster() {
};
}, [userDefaultNamespace]);

React.useEffect(() => {
let timeoutHandle: NodeJS.Timeout | null = null;

if (isEditingDropShellImage()) {
// We store the namespace after a timeout.
timeoutHandle = setTimeout(() => {
if (isValidImageFormat(dropShellImage)) {
storeNewDropShellImage(dropShellImage);
}
}, 1000);
}

return () => {
if (timeoutHandle) {
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
};
}, [dropShellImage]);

function isEditingDefaultNamespace() {
return clusterSettings?.defaultNamespace !== userDefaultNamespace;
}

function isEditingDropShellImage() {
return clusterSettings?.dropShellImage !== dropShellImage;
}

if (!cluster) {
return null;
}
Expand Down Expand Up @@ -163,7 +200,18 @@ export default function SettingsCluster() {
});
}

function storeNewDropShellImage(image: string) {
setClusterSettings((settings: ClusterSettings | null) => {
const newSettings = { ...(settings || {}) };
if (isValidImageFormat(image)) {
newSettings.dropShellImage = image;
}
return newSettings;
});
}

const isValidDefaultNamespace = isValidNamespaceFormat(userDefaultNamespace);
const isValidDropShellImage = isValidImageFormat(dropShellImage);
const isValidNewAllowedNamespace = isValidNamespaceFormat(newAllowedNamespace);
const invalidNamespaceMessage = t(
"translation|Namespaces must contain only lowercase alphanumeric characters or '-', and must start and end with an alphanumeric character."
Expand Down Expand Up @@ -295,6 +343,40 @@ export default function SettingsCluster() {
</>
),
},
{
name: t('translation|Drop Node Shell Image'),
value: (
<TextField
onChange={event => {
let value = event.target.value;
value = value.replace(' ', '');
setDropShellImage(value);
}}
value={dropShellImage}
placeholder={DEFAULT_DROP_SHELL_IMAGE}
error={!isValidImageFormat}
helperText={
isValidDropShellImage
? t(
'translation|The default image is used for dropping a shell into a node (when not specified directly).'
)
: invalidNamespaceMessage
}
InputProps={{
endAdornment: isEditingDropShellImage() ? (
<Icon
width={24}
color={theme.palette.text.secondary}
icon="mdi:progress-check"
/>
) : (
<Icon width={24} icon="mdi:check-bold" />
),
className: classes.input,
}}
/>
),
},
]}
/>
</SectionBox>
Expand Down
Loading

0 comments on commit c171dd8

Please sign in to comment.