Skip to content

Commit

Permalink
fixup! feat: add support a devfite which includes a parent with the s…
Browse files Browse the repository at this point in the history
…torage-type attribute

Signed-off-by: Oleksii Orel <[email protected]>
  • Loading branch information
olexii4 committed Jan 28, 2025
1 parent db32877 commit 4e66ec1
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jest.mock('@/services/helpers/generateName');

describe('FactoryLoaderContainer/prepareDevfile', () => {
describe('DEVWORKSPACE_METADATA_ANNOTATION attribute', () => {
test('add the attribute with annotation', async () => {
test('add the attribute with annotation', () => {
const devfile = {
schemaVersion: '2.2.0',
metadata: {
Expand All @@ -49,7 +49,7 @@ describe('FactoryLoaderContainer/prepareDevfile', () => {
});
});

test('update the attribute with annotation', async () => {
test('update the attribute with annotation', () => {
const customAnnotation = {
'custom-annotation': 'value',
};
Expand Down Expand Up @@ -229,7 +229,7 @@ describe('FactoryLoaderContainer/prepareDevfile', () => {

describe('has parent', () => {
describe('with registryUrl', () => {
it('with storage-type attribute', async () => {
it('with storage-type attribute', () => {
// mute console logs
console.warn = jest.fn();
const devfile = {
Expand Down Expand Up @@ -259,7 +259,7 @@ describe('FactoryLoaderContainer/prepareDevfile', () => {
expect(newDevfile.attributes?.[DEVWORKSPACE_STORAGE_TYPE_ATTR]).toBeUndefined();
});

it('without storage-type attribute', async () => {
it('without storage-type attribute', () => {
const devfile = {
schemaVersion: '2.2.0',
metadata: {
Expand All @@ -282,7 +282,7 @@ describe('FactoryLoaderContainer/prepareDevfile', () => {
});
});
describe('with uri', () => {
it('with storage-type attribute', async () => {
it('with storage-type attribute', () => {
// mute console logs
console.warn = jest.fn();
const devfile = {
Expand Down Expand Up @@ -311,7 +311,7 @@ describe('FactoryLoaderContainer/prepareDevfile', () => {
expect(newDevfile.attributes?.[DEVWORKSPACE_STORAGE_TYPE_ATTR]).toBeUndefined();
});

it('without storage-type attribute', async () => {
it('without storage-type attribute', () => {
const devfile = {
schemaVersion: '2.2.0',
metadata: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { selectPvcStrategy } from '@/store/ServerConfig/selectors';
export type Props = MappedProps & {
readonly: boolean;
storageType?: che.WorkspaceStorageType;
parentStorageType?: che.WorkspaceStorageType;
onSave: (storageType: che.WorkspaceStorageType) => void;
};
export type State = {
Expand Down Expand Up @@ -78,7 +79,7 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
}

public componentDidMount(): void {
const selected = this.props.storageType ? this.props.storageType : this.preferredType;
const selected = this.getSelection();
this.setState({ selected });
}

Expand All @@ -93,43 +94,20 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
}

private getExistingTypes(): {
hasAsync: boolean;
hasPersistent: boolean;
hasEphemeral: boolean;
hasPerUser: boolean;
hasPerWorkspace: boolean;
} {
const hasAsync = this.storageTypes.some(type => type === 'async');
const hasPersistent = this.storageTypes.some(type => type === 'persistent');
const hasEphemeral = this.storageTypes.some(type => type === 'ephemeral');
const hasPerUser = this.storageTypes.some(type => type === 'per-user');
const hasPerWorkspace = this.storageTypes.some(type => type === 'per-workspace');

return { hasAsync, hasPersistent, hasEphemeral, hasPerUser, hasPerWorkspace };
return { hasEphemeral, hasPerUser, hasPerWorkspace };
}

private getInfoModalContent(): React.ReactNode {
const { hasAsync, hasPersistent, hasEphemeral, hasPerUser, hasPerWorkspace } =
this.getExistingTypes();
const { hasEphemeral, hasPerUser, hasPerWorkspace } = this.getExistingTypes();

const asyncTypeDescr = hasAsync ? (
<Text>
<span className={styles.experimentalStorageType}> Experimental feature </span>
<br />
<b>Asynchronous Storage </b>
is combination of Ephemeral and Persistent storages. It allows for faster I / O and keeps
your changes, it does backup the workspace on stop and restores it on start.
</Text>
) : (
''
);
const persistentTypeDescr = hasPersistent ? (
<Text>
<b>Persistent Storage</b> is slow I/O but persistent.
</Text>
) : (
''
);
const ephemeralTypeDescr = hasEphemeral ? (
<Text>
<b>Ephemeral Storage</b> allows for faster I/O but may have limited storage and is not
Expand Down Expand Up @@ -160,11 +138,9 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {

return (
<TextContent>
{persistentTypeDescr}
{perUserTypeDescr}
{perWorkspaceTypeDescr}
{ephemeralTypeDescr}
{asyncTypeDescr}
<Text>
<a rel="noreferrer" target="_blank" href={href}>
Open documentation page
Expand All @@ -175,41 +151,12 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
}

private getSelectorModal(): React.ReactNode {
const { hasAsync, hasPersistent, hasEphemeral, hasPerUser, hasPerWorkspace } =
this.getExistingTypes();
const { hasEphemeral, hasPerUser, hasPerWorkspace } = this.getExistingTypes();

const isDisabled = this.props.parentStorageType !== undefined;
const { isSelectorOpen, selected } = this.state;
const originSelection = this.props.storageType ? this.props.storageType : this.preferredType;
const originSelection = this.getSelection();

const asyncTypeDescr = hasAsync ? (
<Text component={TextVariants.h6}>
<Radio
label="Asynchronous"
name="asynchronous"
id="async-type-radio"
description={`Asynchronous this is combination of Ephemeral and Persistent storage. Allows for faster I/O
and keeps your changes, will backup on stop and restores on start.`}
isChecked={selected === 'async'}
onChange={() => this.setState({ selected: 'async' })}
/>
</Text>
) : (
''
);
const persistentTypeDescr = hasPersistent ? (
<Text component={TextVariants.h6}>
<Radio
label="Persistent"
name="persistent"
id="persistent-type-radio"
description="Persistent Storage slow I/O but persistent."
isChecked={selected === 'persistent'}
onChange={() => this.setState({ selected: 'persistent' })}
/>
</Text>
) : (
''
);
const ephemeralTypeDescr = hasEphemeral ? (
<Text component={TextVariants.h6}>
<Radio
Expand All @@ -218,6 +165,7 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
id="ephemeral-type-radio"
description="Ephemeral Storage allows for faster I/O but may have limited storage and is not persistent."
isChecked={selected === 'ephemeral'}
isDisabled={isDisabled}
onChange={() => this.setState({ selected: 'ephemeral' })}
/>
</Text>
Expand All @@ -232,6 +180,7 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
id="per-user-type-radio"
description="Per-user Storage. One PVC is provisioned per user namespace and used by all workspaces of a given user"
isChecked={selected === 'per-user'}
isDisabled={isDisabled}
onChange={() => this.setState({ selected: 'per-user' })}
/>
</Text>
Expand All @@ -246,6 +195,7 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
id="per-workspace-type-radio"
description="Per-workspace Storage. One PVC is provisioned for each workspace within the namespace."
isChecked={selected === 'per-workspace'}
isDisabled={isDisabled}
onChange={() => this.setState({ selected: 'per-workspace' })}
/>
</Text>
Expand Down Expand Up @@ -278,15 +228,17 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
<Alert
variant={AlertVariant.warning}
className={styles.warningAlert}
title="Note that after changing the storage type you may lose workspace data."
title={
isDisabled
? 'Storage type is already defined in parent, you cannot change it.'
: 'Note that after changing the storage type you may lose workspace data.'
}
isInline
/>
<Text component={TextVariants.h6}>Select the storage type</Text>
{persistentTypeDescr}
{perUserTypeDescr}
{perWorkspaceTypeDescr}
{ephemeralTypeDescr}
{asyncTypeDescr}
</TextContent>
</Modal>
);
Expand All @@ -301,8 +253,18 @@ class StorageTypeFormGroup extends React.PureComponent<Props, State> {
});
}

private getSelection(): che.WorkspaceStorageType {
if (this.props.parentStorageType) {
return this.props.parentStorageType;
} else if (this.props.storageType) {
return this.props.storageType;
}

return this.preferredType;
}

private handleCancelChanges(): void {
const originSelection = this.props.storageType ? this.props.storageType : this.preferredType;
const originSelection = this.getSelection();
this.setState({ selected: originSelection });
this.handleEditToggle(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { InfrastructureNamespaceFormGroup } from '@/pages/WorkspaceDetails/Overv
import { ProjectsFormGroup } from '@/pages/WorkspaceDetails/OverviewTab/Projects';
import StorageTypeFormGroup from '@/pages/WorkspaceDetails/OverviewTab/StorageType';
import WorkspaceNameFormGroup from '@/pages/WorkspaceDetails/OverviewTab/WorkspaceName';
import { getParentDevfile } from '@/services/backend-client/parentDevfileApi';
import { DevfileAdapter } from '@/services/devfile/adapter';
import { DEVWORKSPACE_STORAGE_TYPE_ATTR } from '@/services/devfileApi/devWorkspace/spec/template';
import { DevWorkspaceStatus } from '@/services/helpers/types';
import { che } from '@/services/models';
import { constructWorkspace, Workspace } from '@/services/workspace-adapter';
Expand All @@ -29,6 +32,7 @@ export type Props = {

export type State = {
storageType: che.WorkspaceStorageType;
parentStorageType: che.WorkspaceStorageType | undefined;
};

export class OverviewTab extends React.Component<Props, State> {
Expand All @@ -39,9 +43,28 @@ export class OverviewTab extends React.Component<Props, State> {

this.state = {
storageType: workspace.storageType,
parentStorageType: undefined,
};
}

async componentDidMount(): Promise<void> {
const { workspace } = this.props;
const parent = workspace.ref.spec.template.parent;
if (!parent) {
return;
}
const parentDevfile = await getParentDevfile({ schemaVersion: '2.2.2', parent });
if (parentDevfile) {
const parentDevfileAttributes = DevfileAdapter.getAttributes(parentDevfile);
const parentStorageType = parentDevfileAttributes[DEVWORKSPACE_STORAGE_TYPE_ATTR];
if (parentStorageType) {
this.setState({
parentStorageType,
});
}
}
}

public componentDidUpdate(): void {
const { storageType } = this.state;
const workspace = this.props.workspace;
Expand All @@ -61,7 +84,7 @@ export class OverviewTab extends React.Component<Props, State> {
}

public render(): React.ReactElement {
const { storageType } = this.state;
const { storageType, parentStorageType } = this.state;
const { workspace } = this.props;
const namespace = workspace.namespace;
const projects = workspace.projects;
Expand All @@ -76,6 +99,7 @@ export class OverviewTab extends React.Component<Props, State> {
<StorageTypeFormGroup
readonly={isDeprecated || workspace.status === DevWorkspaceStatus.TERMINATING}
storageType={storageType}
parentStorageType={parentStorageType}
onSave={storageType => this.handleStorageSave(storageType)}
/>
<ProjectsFormGroup projects={projects} />
Expand Down
12 changes: 0 additions & 12 deletions packages/dashboard-frontend/src/services/storageTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,3 @@ export function toTitle(type: che.WorkspaceStorageType): string {
export function getAvailable(): che.WorkspaceStorageType[] {
return ['per-user', 'per-workspace', 'ephemeral'];
}

export function attributesToType(
attrs: che.WorkspaceDevfileAttributes | undefined,
): che.WorkspaceStorageType {
if (attrs?.persistVolumes === 'false') {
if (attrs.asyncPersist === 'true') {
return 'async';
}
return 'ephemeral';
}
return 'persistent';
}

0 comments on commit 4e66ec1

Please sign in to comment.