Skip to content

Commit

Permalink
Add context to VM create with IT
Browse files Browse the repository at this point in the history
Signed-off-by: Aviv Turgeman <aturgema@redhat.com>
  • Loading branch information
avivtur committed May 3, 2023
1 parent 992ff99 commit d0c84cc
Show file tree
Hide file tree
Showing 39 changed files with 484 additions and 650 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
"validator": "^13.7.0",
"xbytes": "^1.7.0",
"xterm": "^4.18.0",
"xterm-addon-fit": "^0.5.0"
"xterm-addon-fit": "^0.5.0",
"zustand": "^4.3.7"
},
"devDependencies": {
"@cypress/webpack-preprocessor": "^5.11.0",
Expand Down
50 changes: 13 additions & 37 deletions src/utils/components/SSHSecretSection/SSHSecretSection.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
import React, { Dispatch, FC, SetStateAction, useEffect, useState } from 'react';
import React, { FC, useState } from 'react';

import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
import { modelToGroupVersionKind, SecretModel } from '@kubevirt-ui/kubevirt-api/console';
import { IoK8sApiCoreV1Secret } from '@kubevirt-ui/kubevirt-api/kubernetes';
import SecretDropdown from '@kubevirt-utils/components/SSHSecretSection/utils/components/SecretDropdown/SecretDropdown';
import SecretSelectionRadioGroup from '@kubevirt-utils/components/SSHSecretSection/utils/components/SecretSelectionRadioGroup';
import SSHKeyUpload from '@kubevirt-utils/components/SSHSecretSection/utils/components/SSHKeyUpload/SSHKeyUpload';
import {
SecretSelectionOption,
SSHSecretDetails,
} from '@kubevirt-utils/components/SSHSecretSection/utils/types';
import SecretDropdown from '@kubevirt-utils/components/SSHSecretSection/components/SecretDropdown/SecretDropdown';
import SecretSelectionRadioGroup from '@kubevirt-utils/components/SSHSecretSection/components/SecretSelectionRadioGroup';
import SSHKeyUpload from '@kubevirt-utils/components/SSHSecretSection/components/SSHKeyUpload/SSHKeyUpload';
import { SecretSelectionOption } from '@kubevirt-utils/components/SSHSecretSection/utils/types';
import { ALL_NAMESPACES_SESSION_KEY } from '@kubevirt-utils/hooks/constants';
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { useK8sWatchResource } from '@openshift-console/dynamic-plugin-sdk';
import { useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk-internal';
import { Grid, GridItem, PopoverPosition } from '@patternfly/react-core';

import HelpTextIcon from '../HelpTextIcon/HelpTextIcon';

import './SSHSecretSection.scss';

type SSHSecretSectionProps = {
sshSecretDetails: SSHSecretDetails;
setSSHSecretDetails: Dispatch<SetStateAction<SSHSecretDetails>>;
};

const SSHSecretSection: FC<SSHSecretSectionProps> = ({ sshSecretDetails, setSSHSecretDetails }) => {
const [activeNamespace] = useActiveNamespace();
const SSHSecretSection: FC = () => {
const { t } = useKubevirtTranslation();
const { activeNamespace } = useInstanceTypeVMStore();
const [secretSelectionOption, setSecretSelectionOption] = useState<SecretSelectionOption>(
SecretSelectionOption.none,
);

const secretsResourceData = useK8sWatchResource<IoK8sApiCoreV1Secret[]>({
const [secrets, ...loadedAndErrorData] = useK8sWatchResource<IoK8sApiCoreV1Secret[]>({
groupVersionKind: modelToGroupVersionKind(SecretModel),
isList: true,
...(activeNamespace !== ALL_NAMESPACES_SESSION_KEY && {
namespace: activeNamespace,
}),
});

const setSelectedSecretName = (secretName) => {
setSSHSecretDetails({ sshSecretName: secretName, sshSecretKey: '' });
};

// Inputs should not persist between changes of secretSelectionOption
useEffect(() => {
setSSHSecretDetails({ sshSecretName: '', sshSecretKey: '' });
}, [secretSelectionOption, setSSHSecretDetails]);

return (
<Grid className="ssh-secret-section">
<GridItem className="ssh-secret-section__title">
Expand All @@ -64,18 +48,10 @@ const SSHSecretSection: FC<SSHSecretSectionProps> = ({ sshSecretDetails, setSSHS
</GridItem>
<GridItem span={12} className="ssh-secret-section__body">
{secretSelectionOption === SecretSelectionOption.useExisting && (
<SecretDropdown
secretsResourceData={secretsResourceData}
selectedSecretName={sshSecretDetails?.sshSecretName}
onSelectSecret={setSelectedSecretName}
/>
<SecretDropdown secretsResourceData={[secrets, ...loadedAndErrorData]} />
)}
{secretSelectionOption === SecretSelectionOption.addNew && (
<SSHKeyUpload
sshSecretCredentials={sshSecretDetails}
setSSHSecretCredentials={setSSHSecretDetails}
secrets={secretsResourceData?.[0]}
/>
<SSHKeyUpload secrets={secrets} />
)}
</GridItem>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { Dispatch, SetStateAction, useState } from 'react';
import React, { FC, useState } from 'react';

import { SSHSecretCredentials } from '@catalog/CreateFromInstanceTypes/components/VMDetailsSection/components/SSHKeySection/utils/types';
import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
import { instanceTypeActionType } from '@catalog/CreateFromInstanceTypes/state/utils/types';
import { IoK8sApiCoreV1Secret } from '@kubevirt-ui/kubevirt-api/kubernetes';
import { validateSecretName } from '@kubevirt-utils/components/SSHSecretSection/utils/utils';
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { validateSSHPublicKey } from '@kubevirt-utils/utils/utils';
import {
FileUpload,
Expand All @@ -19,16 +20,13 @@ import './SSHKeyUpload.scss';

type SSHKeyUploadProps = {
secrets: IoK8sApiCoreV1Secret[];
sshSecretCredentials: SSHSecretCredentials;
setSSHSecretCredentials: Dispatch<SetStateAction<SSHSecretCredentials>>;
};

const SSHKeyUpload: React.FC<SSHKeyUploadProps> = ({
secrets,
sshSecretCredentials,
setSSHSecretCredentials,
}) => {
const { sshSecretName, sshSecretKey } = sshSecretCredentials;
const SSHKeyUpload: FC<SSHKeyUploadProps> = ({ secrets }) => {
const { t } = useKubevirtTranslation();
const { instanceTypeVMState, setInstanceTypeVMState } = useInstanceTypeVMStore();
const { sshSecretCredentials } = instanceTypeVMState;
const { sshSecretKey, sshSecretName } = sshSecretCredentials;
const [isValidName, setIsValidName] = useState<boolean>(true);
const [isValidKey, setIsValidKey] = useState<boolean>(true);
const [isLoading, setIsLoading] = useState<boolean>(false);
Expand All @@ -40,9 +38,12 @@ const SSHKeyUpload: React.FC<SSHKeyUploadProps> = ({
className="ssh-key-upload__file-upload"
type="text"
value={sshSecretKey}
onChange={(v: string) => {
setIsValidKey(validateSSHPublicKey(v));
setSSHSecretCredentials((prevState) => ({ ...prevState, sshSecretKey: v?.trim() }));
onChange={(sshPublicKey: string) => {
setIsValidKey(validateSSHPublicKey(sshPublicKey));
setInstanceTypeVMState({
type: instanceTypeActionType.setSSHCredentials,
payload: { ...sshSecretCredentials, sshSecretKey: sshPublicKey?.trim() },
});
}}
onReadStarted={() => setIsLoading(true)}
onReadFinished={() => setIsLoading(false)}
Expand Down Expand Up @@ -71,9 +72,12 @@ const SSHKeyUpload: React.FC<SSHKeyUploadProps> = ({
name="new-secret-name"
value={sshSecretName}
isRequired
onChange={(v: string) => {
setIsValidName(validateSecretName(v, secrets));
setSSHSecretCredentials((prevState) => ({ ...prevState, sshSecretName: v }));
onChange={(secretName: string) => {
setIsValidName(validateSecretName(secretName, secrets));
setInstanceTypeVMState({
type: instanceTypeActionType.setSSHCredentials,
payload: { ...sshSecretCredentials, sshSecretName: secretName },
});
}}
/>
</FormGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
import React, { FC, ReactElement, useState } from 'react';

import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
import { instanceTypeActionType } from '@catalog/CreateFromInstanceTypes/state/utils/types';
import { IoK8sApiCoreV1Secret } from '@kubevirt-ui/kubevirt-api/kubernetes';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { getName } from '@kubevirt-utils/resources/shared';
import { validateSSHPublicKey } from '@kubevirt-utils/utils/utils';
import { WatchK8sResult } from '@openshift-console/dynamic-plugin-sdk';
import { Alert, AlertVariant, Select, SelectOption, SelectVariant } from '@patternfly/react-core';

import Loading from '../../../../Loading/Loading';
import { decodeSecret } from '../../utils';
import Loading from '../../../Loading/Loading';
import { decodeSecret } from '../../utils/utils';

type SecretDropdownProps = {
secretsResourceData: WatchK8sResult<IoK8sApiCoreV1Secret[]>;
selectedSecretName: string;
onSelectSecret: (secretName: string) => void;
id?: string;
};

const SecretDropdown: FC<SecretDropdownProps> = ({
secretsResourceData,
selectedSecretName,
onSelectSecret,
id,
}) => {
const SecretDropdown: FC<SecretDropdownProps> = ({ secretsResourceData, id }) => {
const { t } = useKubevirtTranslation();
const [isOpen, setIsOpen] = useState(false);

const [allSecrets, secretsLoaded, secretsError] = secretsResourceData;
const { instanceTypeVMState, setInstanceTypeVMState } = useInstanceTypeVMStore();

const sshSecretName = instanceTypeVMState?.sshSecretCredentials?.sshSecretName;

const [allSecrets = [], secretsLoaded, secretsError] = secretsResourceData;
const sshKeySecrets = allSecrets
? allSecrets
?.filter((secret) => secret?.data && validateSSHPublicKey(decodeSecret(secret)))
?.sort((a, b) => a?.metadata?.name.localeCompare(b?.metadata?.name))
: [];
?.filter((secret) => secret?.data && validateSSHPublicKey(decodeSecret(secret)))
?.sort((a, b) => a?.metadata?.name.localeCompare(b?.metadata?.name));

const onSelect = (event, newSecretName) => {
onSelectSecret(newSecretName);
const onSelect = (_, newSecretName: string) => {
setInstanceTypeVMState({
type: instanceTypeActionType.setSSHCredentials,
payload: { sshSecretName: newSecretName, sshSecretKey: '' },
});
setIsOpen(false);
};

Expand Down Expand Up @@ -63,7 +63,7 @@ const SecretDropdown: FC<SecretDropdownProps> = ({
variant={SelectVariant.single}
onFilter={filterSecrets}
hasInlineFilter
selections={selectedSecretName}
selections={sshSecretName}
placeholderText={t('--- Select secret ---')}
maxHeight={400}
id={id || 'select-secret'}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { Dispatch, FC, SetStateAction } from 'react';
import React, { Dispatch, FC, SetStateAction, useCallback } from 'react';

import { useInstanceTypeVMStore } from '@catalog/CreateFromInstanceTypes/state/useInstanceTypeVMStore';
import { instanceTypeActionType } from '@catalog/CreateFromInstanceTypes/state/utils/types';
import { SecretSelectionOption } from '@kubevirt-utils/components/SSHSecretSection/utils/types';
import { t } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { Radio, Split, SplitItem } from '@patternfly/react-core';
Expand All @@ -13,6 +15,24 @@ const SecretSelectionRadioGroup: FC<SecretSelectionRadioGroupProps> = ({
selectedOption,
setSelectedOption,
}) => {
const { setInstanceTypeVMState } = useInstanceTypeVMStore();

// Inputs should not persist between changes of secretSelectionOption
const onSelectSecretOption = useCallback(
(secretOption: SecretSelectionOption) => {
setSelectedOption((prevSecretOption) => {
if (prevSecretOption !== secretOption) {
setInstanceTypeVMState({
type: instanceTypeActionType.setSSHCredentials,
payload: { sshSecretName: '', sshSecretKey: '' },
});
}

return secretOption;
});
},
[setInstanceTypeVMState, setSelectedOption],
);
return (
<Split hasGutter>
<SplitItem>
Expand All @@ -21,7 +41,7 @@ const SecretSelectionRadioGroup: FC<SecretSelectionRadioGroupProps> = ({
id={SecretSelectionOption.none}
name="ssh-secret-selection"
label={t('None')}
onClick={() => setSelectedOption(SecretSelectionOption.none)}
onClick={() => onSelectSecretOption(SecretSelectionOption.none)}
/>
</SplitItem>
<SplitItem>
Expand All @@ -30,7 +50,7 @@ const SecretSelectionRadioGroup: FC<SecretSelectionRadioGroupProps> = ({
id={SecretSelectionOption.useExisting}
name="ssh-secret-selection"
label={t('Use existing')}
onClick={() => setSelectedOption(SecretSelectionOption.useExisting)}
onClick={() => onSelectSecretOption(SecretSelectionOption.useExisting)}
/>
</SplitItem>
<SplitItem>
Expand All @@ -39,7 +59,7 @@ const SecretSelectionRadioGroup: FC<SecretSelectionRadioGroupProps> = ({
id={SecretSelectionOption.addNew}
name="ssh-secret-selection"
label={t('Add new')}
onClick={() => setSelectedOption(SecretSelectionOption.addNew)}
onClick={() => onSelectSecretOption(SecretSelectionOption.addNew)}
/>
</SplitItem>
</Split>
Expand Down
15 changes: 4 additions & 11 deletions src/views/bootablevolumes/list/BootableVolumesList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import React, { FC, useState } from 'react';

import AddBootableVolumeButton from '@catalog/CreateFromInstanceTypes/components/AddBootableVolumeButton/AddBootableVolumeButton';
import useInstanceTypesAndPreferences from '@catalog/CreateFromInstanceTypes/hooks/useInstanceTypesAndPreferences';
import useInstanceTypesAndPreferences from '@catalog/CreateFromInstanceTypes/state/hooks/useInstanceTypesAndPreferences';
import DeveloperPreviewLabel from '@kubevirt-utils/components/DeveloperPreviewLabel/DeveloperPreviewLabel';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import { DataSourceModelGroupVersionKind, DataSourceModelRef } from '@kubevirt-utils/models';
import {
convertResourceArrayToMap,
getAvailableOrCloningDataSources,
} from '@kubevirt-utils/resources/shared';
import { getAvailableOrCloningDataSources } from '@kubevirt-utils/resources/shared';
import {
K8sResourceCommon,
ListPageBody,
Expand All @@ -30,7 +27,7 @@ import './BootableVolumesList.scss';
const BootableVolumesList: FC = () => {
const { t } = useKubevirtTranslation();
const [dataSources, loadedDataSources, loadErrorDataSources] = useBootableVolumes();
const { preferences, loadError } = useInstanceTypesAndPreferences();
const { preferences } = useInstanceTypesAndPreferences();
const [data, filteredData, onFilterChange] = useListPageFilter(
getAvailableOrCloningDataSources(dataSources),
useBootableVolumesFilters(),
Expand All @@ -50,11 +47,7 @@ const BootableVolumesList: FC = () => {
return (
<>
<ListPageHeader title={t('Bootable volumes')} badge={<DeveloperPreviewLabel />}>
<AddBootableVolumeButton
preferencesNames={Object.keys(convertResourceArrayToMap(preferences))}
loadError={loadError}
buttonVariant={ButtonVariant.primary}
/>
<AddBootableVolumeButton buttonVariant={ButtonVariant.primary} />
</ListPageHeader>

<ListPageBody>
Expand Down
Loading

0 comments on commit d0c84cc

Please sign in to comment.