Skip to content

Commit

Permalink
fixup! Show warning banner on user namespace FailedScheduling event
Browse files Browse the repository at this point in the history
  • Loading branch information
vinokurig committed Oct 10, 2024
1 parent cedf82f commit 1434618
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,75 @@
* Red Hat, Inc. - initial API and implementation
*/

import { api } from '@eclipse-che/common';
import { EventPhase } from '@eclipse-che/common/lib/dto/api/webSocket';
import { render, RenderResult, screen, waitFor } from '@testing-library/react';
import { screen, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';

import BannerAlertNoNodeAvailable from '@/components/BannerAlert/NoNodeAvailable';
import { container } from '@/inversify.config';
import { WebsocketClient } from '@/services/backend-client/websocketClient';
import getComponentRenderer from '@/services/__mocks__/getComponentRenderer';
import { DevWorkspaceBuilder } from '@/store/__mocks__/devWorkspaceBuilder';
import { FakeStoreBuilder } from '@/store/__mocks__/storeBuilder';

const { renderComponent } = getComponentRenderer(getComponent);
const text =
'"FailedScheduling" event occurred. If cluster autoscaler is enabled it might be provisioning a new node now and workspace startup will take longer than usual.';
const websocketClient = container.get(WebsocketClient);

describe('BannerAlertNoNodeAvailable component', () => {
it('should show alert when failedScheduling event is received and hide alert when workspace has started', async () => {
const { reRenderComponent } = renderComponent(new FakeStoreBuilder().build());

const events = [
{
reason: 'FailedScheduling',
message: 'No preemption victims found for incoming pod',
metadata: { uid: 'uid' },
} as any,
];

renderComponent(new FakeStoreBuilder().withEvents({ events }).build());
const store = new FakeStoreBuilder().withEvents({ events }).build();
reRenderComponent(store);

await waitFor(() => expect(screen.queryAllByText(text).length).toEqual(1));
});

// Dispatch workspace started event to clear the state.
await (websocketClient as any).messageHandler.listeners.get(
api.webSocket.Channel.DEV_WORKSPACE,
)![0]({
devWorkspace: { status: { phase: 'Running' } },
} as any);

// wait banner to hide
await new Promise(resolve => setTimeout(resolve, 1000));
it('should hide alert when workspace has started', async () => {
const { reRenderComponent } = renderComponent(new FakeStoreBuilder().build());

await waitFor(() => expect(screen.queryAllByText(text).length).toEqual(0));
});
const events = [
{
reason: 'FailedScheduling',
message: 'No preemption victims found for incoming pod',
metadata: { uid: 'uid' },
} as any,
];
const workspaces = [
new DevWorkspaceBuilder().withStatus({ phase: 'STARTING', devworkspaceId: 'id' }).build(),
];
const store = new FakeStoreBuilder()
.withEvents({ events })
.withDevWorkspaces({ workspaces })
.build();
reRenderComponent(store);

it('should not show alert if user namespace event is undefined', async () => {
const events = [{} as any];
await waitFor(() => expect(screen.queryAllByText(text).length).toEqual(1));

renderComponent(new FakeStoreBuilder().withEvents({ events }).build());
const nextWorkspaces = [
new DevWorkspaceBuilder().withStatus({ phase: 'RUNNING', devworkspaceId: 'id' }).build(),
];
const nextStore = new FakeStoreBuilder()
.withEvents({ events })
.withDevWorkspaces({ workspaces: nextWorkspaces })
.build();
reRenderComponent(nextStore);

await waitFor(() => expect(screen.queryAllByText(text).length).toEqual(0));
});
});

function renderComponent(store: Store<any, any>): RenderResult {
const component = (
function getComponent(store: Store<any, any>) {
return (
<Provider store={store}>
<BannerAlertNoNodeAvailable />
</Provider>
);
return render(<Provider store={store}>{component}</Provider>);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@
* Red Hat, Inc. - initial API and implementation
*/

import { api } from '@eclipse-che/common';
import { Banner } from '@patternfly/react-core';
import React from 'react';
import { connect, ConnectedProps } from 'react-redux';

import { container } from '@/inversify.config';
import { WebsocketClient } from '@/services/backend-client/websocketClient';
import { ChannelListener } from '@/services/backend-client/websocketClient/messageHandler';
import { DevWorkspaceStatus } from '@/services/helpers/types';
import { AppState } from '@/store';
import { selectAllEvents } from '@/store/Events/selectors';
import { selectAllWorkspaces } from '@/store/Workspaces/selectors';
Expand All @@ -26,7 +25,6 @@ type Props = MappedProps;

type State = {
startingWorkspaces: string[];
eventsLength: number;
};

class BannerAlertNoNodeAvailable extends React.PureComponent<Props, State> {
Expand All @@ -37,49 +35,57 @@ class BannerAlertNoNodeAvailable extends React.PureComponent<Props, State> {
this.websocketClient = container.get(WebsocketClient);
this.state = {
startingWorkspaces: [],
eventsLength: 0,
};
}

public async componentDidMount() {
const devWorkspaceListener: ChannelListener = message => {
const devWorkspace = (message as api.webSocket.DevWorkspaceMessage).devWorkspace;
if (devWorkspace.status === undefined) {
return;
} else if (
devWorkspace.status.phase === 'Running' &&
this.state.startingWorkspaces.length > 0
) {
this.setState({ startingWorkspaces: [] });
}
};
this.websocketClient.addChannelMessageListener(
api.webSocket.Channel.DEV_WORKSPACE,
devWorkspaceListener,
);
componentDidUpdate(prevProps: Readonly<Props>) {
this.handleAllEventsChange(prevProps);
this.handleAllWorkspacesChange(prevProps);
}

private handleAllEventsChange() {
private handleAllEventsChange(prevProps: Readonly<Props>) {
const allEvents = this.props.allEvents;
if (allEvents.length === this.state.eventsLength) {
const prevAllEvents = prevProps.allEvents;

if (JSON.stringify(allEvents) === JSON.stringify(prevAllEvents)) {
return;
}

const event = allEvents[allEvents.length - 1];
if (event.message === undefined) {
return;
} else if (
if (
event.message !== undefined &&
event.reason === 'FailedScheduling' &&
event.message.indexOf('No preemption victims found for incoming pod') > -1 &&
this.state.startingWorkspaces.length === 0
) {
this.setState({ startingWorkspaces: [event.metadata!.uid!] });
this.setState({ eventsLength: allEvents.length });
}
}

render() {
this.handleAllEventsChange();
private handleAllWorkspacesChange(prevProps: Readonly<Props>) {
const prevAllWorkspaces = prevProps.allWorkspaces;
const allWorkspaces = this.props.allWorkspaces;

if (JSON.stringify(allWorkspaces) === JSON.stringify(prevAllWorkspaces)) {
return;
}

if (
allWorkspaces.some(
workspace =>
workspace.status === DevWorkspaceStatus.RUNNING &&
prevAllWorkspaces.find(
prevWorkspace =>
prevWorkspace.id === workspace.id &&
prevWorkspace.status === DevWorkspaceStatus.STARTING,
),
)
) {
this.setState({ startingWorkspaces: [] });
}
}

render() {
if (this.state.startingWorkspaces.length === 0) {
return null;
}
Expand Down

0 comments on commit 1434618

Please sign in to comment.