@gen3/workspaces 0.12.50 → 0.12.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TODO.md +2 -2
- package/dist/dts/components/HorizontalAccordian.js +2 -2
- package/dist/dts/components/HorizontalAccordian.js.map +1 -1
- package/dist/dts/components/KernelLifecyclePanel/ActiveKernelsPanel.d.ts.map +1 -1
- package/dist/dts/components/KernelLifecyclePanel/ActiveKernelsPanel.js +2 -0
- package/dist/dts/components/KernelLifecyclePanel/ActiveKernelsPanel.js.map +1 -1
- package/dist/dts/components/MicroContainerReduxPanel.d.ts.map +1 -1
- package/dist/dts/components/MicroContainerReduxPanel.js +13 -13
- package/dist/dts/components/MicroContainerReduxPanel.js.map +1 -1
- package/dist/dts/core/hatcheryApi.d.ts.map +1 -1
- package/dist/dts/core/hatcheryApi.js +21 -6
- package/dist/dts/core/hatcheryApi.js.map +1 -1
- package/dist/dts/core/jegGatewayApi.d.ts +67 -0
- package/dist/dts/core/jegGatewayApi.d.ts.map +1 -1
- package/dist/dts/core/jegGatewayApi.js +2 -0
- package/dist/dts/core/jegGatewayApi.js.map +1 -1
- package/dist/dts/providers/useGatewayConnectionStatus.d.ts.map +1 -1
- package/dist/dts/providers/useGatewayConnectionStatus.js +5 -1
- package/dist/dts/providers/useGatewayConnectionStatus.js.map +1 -1
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.d.ts.map +1 -1
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.js +69 -41
- package/dist/dts/providers/useJEGWorkspaceResourceMonitor.js.map +1 -1
- package/dist/dts/providers/useMicroContainerRedux.d.ts.map +1 -1
- package/dist/dts/providers/useMicroContainerRedux.js +7 -14
- package/dist/dts/providers/useMicroContainerRedux.js.map +1 -1
- package/dist/dts/server/workspaceAssetsHandler.js +2 -2
- package/dist/dts/server/workspaceAssetsHandler.js.map +1 -1
- package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.d.ts.map +1 -1
- package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.js +1 -2
- package/dist/dts/workspace/WorkspaceLayout/SettingsPanel.js.map +1 -1
- package/dist/index/cjs/index.js +128 -83
- package/dist/index/cjs/index.js.map +1 -1
- package/dist/index/esm/index.js +128 -83
- package/dist/index/esm/index.js.map +1 -1
- package/dist/server/cjs/server.js +2 -2
- package/dist/server/cjs/server.js.map +1 -1
- package/dist/server/esm/server.js +2 -2
- package/dist/server/esm/server.js.map +1 -1
- package/package.json +4 -4
package/dist/index/esm/index.js
CHANGED
|
@@ -3,10 +3,10 @@ import React, { useCallback, useMemo, useState, useRef, useEffect, createContext
|
|
|
3
3
|
import { mergeDefaultTailwindClassnames, ACTIVITY_CHANNEL, convertSecondsToMilliseconds, InfoRolloverButton, OverflowTooltippedLabel, PanelErrorBoundary, ProtectedContent, FixedNavPageLayout, ContentSource, getNavPageLayoutPropsFromConfig } from '@gen3/frontend';
|
|
4
4
|
import { Card, Stack, Badge, Text, List, Group, Tooltip, Button, LoadingOverlay, Alert, Loader, Divider, Select, ActionIcon, TextInput, Textarea, Table, Scroller, Tabs, Center } from '@mantine/core';
|
|
5
5
|
import { Icon } from '@iconify-icon/react';
|
|
6
|
+
import { useInterval, useElementSize, useDisclosure, useResizeObserver } from '@mantine/hooks';
|
|
6
7
|
import { GEN3_API, GEN3_WORKSPACE_API, gen3Api, useCoreSelector, selectJEGActiveWorkspaceStatus, useCoreDispatch, setJEGActiveWorkspaceStatus, WorkspaceStatus, setJEGRequestedWorkspaceStatus, RequestedWorkspaceStatus, setJEGActiveWorkspaceId, isFetchBaseQueryError, selectJEGRequestedWorkspaceStatus, selectJEGRequestedWorkspaceStatusTimestamp, isTimeGreaterThan, formatUptimeInMinutes, selectJEGKernelIds, removeManyJEGActiveKernels, upsertManyJEGActiveKernels, normalizeRtkError, selectAllJEGKernels, addJEGActiveKernel, selectWorkspaceTier, setWorkspaceTier, selectWorkspaceFullscreen, setWorkspaceFullscreen, useGetDictionaryFromUrlQuery, useUserAuth, GEN3_COMMONS_NAME } from '@gen3/core';
|
|
7
8
|
import { useDeepCompareEffect } from 'use-deep-compare';
|
|
8
9
|
import { notifications } from '@mantine/notifications';
|
|
9
|
-
import { useElementSize, useDisclosure, useResizeObserver } from '@mantine/hooks';
|
|
10
10
|
import { IoSettingsSharp } from 'react-icons/io5';
|
|
11
11
|
import { BsFillInfoCircleFill } from 'react-icons/bs';
|
|
12
12
|
import { MdKeyboardArrowDown, MdKeyboardArrowRight } from 'react-icons/md';
|
|
@@ -337,22 +337,34 @@ const HatcheryWithTags = gen3Api.enhanceEndpoints({
|
|
|
337
337
|
'Hatchery'
|
|
338
338
|
]
|
|
339
339
|
});
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
/*
|
|
341
|
+
* Hatchery states:
|
|
342
|
+
*
|
|
343
|
+
* `Terminating` - The workspace is shutting down
|
|
344
|
+
* `Launching` - The workspace is starting up
|
|
345
|
+
* `Stopped` - The workspace is in a failed state and must be terminated
|
|
346
|
+
* `Running` - The workspace is running and ready to be used
|
|
347
|
+
* `Not Found` - The workspace could not be found
|
|
348
|
+
*/ const StatusStringToHatcheryServiceStatus = {
|
|
349
|
+
launching: [
|
|
342
350
|
'launching',
|
|
343
351
|
'pending',
|
|
344
|
-
'starting'
|
|
352
|
+
'starting'
|
|
353
|
+
],
|
|
354
|
+
running: [
|
|
345
355
|
'running',
|
|
346
356
|
'ready'
|
|
347
357
|
],
|
|
348
358
|
stopped: [
|
|
349
|
-
'
|
|
350
|
-
'stopped',
|
|
351
|
-
'terminated'
|
|
359
|
+
'stopped'
|
|
352
360
|
],
|
|
353
|
-
|
|
361
|
+
terminating: [
|
|
354
362
|
'terminating',
|
|
355
363
|
'stopping'
|
|
364
|
+
],
|
|
365
|
+
unknown: [
|
|
366
|
+
'not found',
|
|
367
|
+
'unknown'
|
|
356
368
|
]
|
|
357
369
|
};
|
|
358
370
|
const hatcheryApi = HatcheryWithTags.injectEndpoints({
|
|
@@ -395,17 +407,22 @@ const hatcheryApi = HatcheryWithTags.injectEndpoints({
|
|
|
395
407
|
transformResponse: (response)=>{
|
|
396
408
|
const results = (response.status || 'unknown').toLowerCase();
|
|
397
409
|
let hatcheryStatus = HatcheryServiceState.unknown;
|
|
398
|
-
if (results
|
|
410
|
+
if (StatusStringToHatcheryServiceStatus.unknown.includes(results)) {
|
|
411
|
+
hatcheryStatus = HatcheryServiceState.unknown;
|
|
412
|
+
}
|
|
413
|
+
if (StatusStringToHatcheryServiceStatus.launching.includes(results)) {
|
|
414
|
+
hatcheryStatus = HatcheryServiceState.launching;
|
|
415
|
+
}
|
|
399
416
|
if (StatusStringToHatcheryServiceStatus.running.includes(results)) {
|
|
400
417
|
hatcheryStatus = HatcheryServiceState.running;
|
|
401
418
|
}
|
|
402
419
|
if (StatusStringToHatcheryServiceStatus.stopped.includes(results)) {
|
|
403
420
|
hatcheryStatus = HatcheryServiceState.stopped;
|
|
404
421
|
}
|
|
405
|
-
if (StatusStringToHatcheryServiceStatus.
|
|
422
|
+
if (StatusStringToHatcheryServiceStatus.terminating.includes(results)) {
|
|
406
423
|
hatcheryStatus = HatcheryServiceState.terminating;
|
|
407
424
|
}
|
|
408
|
-
// return other
|
|
425
|
+
// return other stats from the hatchery response
|
|
409
426
|
return {
|
|
410
427
|
status: hatcheryStatus,
|
|
411
428
|
idleTimeLimit: response.idleTimeLimit,
|
|
@@ -557,7 +574,7 @@ const { useDoesHatcheryOptionExistsQuery, useHatcheryOptionsQuery, useLazyHatche
|
|
|
557
574
|
const workspaceContainerStatus = useCoreSelector(selectJEGActiveWorkspaceStatus);
|
|
558
575
|
const [containerHash, setContainerHash] = useState(null);
|
|
559
576
|
const coreDispatch = useCoreDispatch();
|
|
560
|
-
// get the options for the workspace could move to parent
|
|
577
|
+
// get the options for the workspace could move to parent
|
|
561
578
|
const { data: optionData, error: optionsError } = useDoesHatcheryOptionExistsQuery(tag, {
|
|
562
579
|
skip: containerHash !== null
|
|
563
580
|
});
|
|
@@ -595,7 +612,8 @@ const { useDoesHatcheryOptionExistsQuery, useHatcheryOptionsQuery, useLazyHatche
|
|
|
595
612
|
// launch error, will show error message and then reset
|
|
596
613
|
coreDispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.LaunchError));
|
|
597
614
|
} else {
|
|
598
|
-
|
|
615
|
+
// Keep status as Launching — the monitor will promote to Running
|
|
616
|
+
// once the pod actually reports running status
|
|
599
617
|
coreDispatch(setJEGActiveWorkspaceId({
|
|
600
618
|
id: containerHash ?? 'default'
|
|
601
619
|
}));
|
|
@@ -614,18 +632,11 @@ const { useDoesHatcheryOptionExistsQuery, useHatcheryOptionsQuery, useLazyHatche
|
|
|
614
632
|
]);
|
|
615
633
|
const terminate = useCallback(async ()=>{
|
|
616
634
|
if (!enabled || workspaceContainerStatus === WorkspaceStatus.Terminating) return; // skip if already terminating
|
|
617
|
-
// coreDispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.Running));
|
|
618
635
|
const containerId = containerHash ? encodeURIComponent(containerHash) : '';
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
coreDispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Terminate));
|
|
624
|
-
coreDispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.Terminating));
|
|
625
|
-
} catch (_error) {
|
|
626
|
-
coreDispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Terminate));
|
|
627
|
-
coreDispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.Terminating));
|
|
628
|
-
}
|
|
636
|
+
// Fire-and-forget: errors are handled by the useDeepCompareEffect watching isTerminateError
|
|
637
|
+
terminateWorkspace(containerId);
|
|
638
|
+
coreDispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Terminate));
|
|
639
|
+
coreDispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.Terminating));
|
|
629
640
|
}, [
|
|
630
641
|
enabled,
|
|
631
642
|
workspaceContainerStatus,
|
|
@@ -682,10 +693,10 @@ const notifyUser = (title, message, status = 0)=>{
|
|
|
682
693
|
// TODO: convert to seconds/minutes for readability
|
|
683
694
|
const WorkspacePollingInterval = {
|
|
684
695
|
[WorkspaceStatus.NotFound]: 0,
|
|
685
|
-
[WorkspaceStatus.Launching]: convertSecondsToMilliseconds(
|
|
686
|
-
[WorkspaceStatus.Terminating]: convertSecondsToMilliseconds(
|
|
687
|
-
[WorkspaceStatus.Running]: convertSecondsToMilliseconds(
|
|
688
|
-
[WorkspaceStatus.Stopped]: convertSecondsToMilliseconds(
|
|
696
|
+
[WorkspaceStatus.Launching]: convertSecondsToMilliseconds(5),
|
|
697
|
+
[WorkspaceStatus.Terminating]: convertSecondsToMilliseconds(5),
|
|
698
|
+
[WorkspaceStatus.Running]: convertSecondsToMilliseconds(60),
|
|
699
|
+
[WorkspaceStatus.Stopped]: convertSecondsToMilliseconds(60),
|
|
689
700
|
[WorkspaceStatus.Errored]: convertSecondsToMilliseconds(10),
|
|
690
701
|
[WorkspaceStatus.LaunchError]: convertSecondsToMilliseconds(10),
|
|
691
702
|
[WorkspaceStatus.TerminateError]: convertSecondsToMilliseconds(10),
|
|
@@ -696,7 +707,8 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
696
707
|
* Monitors resource usage.
|
|
697
708
|
* Handles state changes and polls for workspace status using the JEG APIs
|
|
698
709
|
*/ const useJEGWorkspaceResourceMonitor = (workspaceId = null, monitorWorkspace)=>{
|
|
699
|
-
|
|
710
|
+
// start with default polling interval of 1 second
|
|
711
|
+
const [pollingInterval, setPollingInterval] = useState(convertSecondsToMilliseconds(1));
|
|
700
712
|
const [error, setError] = useState(null);
|
|
701
713
|
const { data: workspaceStatusData, isError: isWorkspaceStatusError, error: workspaceStatusError } = useHatcheryStatusQuery(workspaceId, {
|
|
702
714
|
pollingInterval: pollingInterval,
|
|
@@ -742,17 +754,37 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
742
754
|
error
|
|
743
755
|
]);
|
|
744
756
|
useDeepCompareEffect(()=>{
|
|
757
|
+
// No status data means we are not polling
|
|
745
758
|
if (!workspaceStatusData) return;
|
|
746
|
-
// LaunchError is set client-side by the launch() call and cleared by the
|
|
759
|
+
// LaunchError is set client-side by the launch() call from useMicroContainerRedux and cleared by the
|
|
747
760
|
// auto-reset timer in the panel. Polling data must not override it or the
|
|
748
|
-
// timer will be
|
|
761
|
+
// timer will be canceled before it fires.
|
|
749
762
|
if (activeStatus === WorkspaceStatus.LaunchError) return;
|
|
750
763
|
const workspaceQueryStatus = hatcheryStateToWorkspaceStatus(workspaceStatusData?.status);
|
|
764
|
+
// handle cases of unknown, running, terminating, or launching
|
|
765
|
+
if (workspaceStatusData.status === HatcheryServiceState.unknown) {
|
|
766
|
+
// check for unknown state
|
|
767
|
+
// NotFound means pod is not running
|
|
768
|
+
// either starting up
|
|
769
|
+
// or finally terminated.
|
|
770
|
+
if (requestedStatus === RequestedWorkspaceStatus.Launch) {
|
|
771
|
+
// if the workspace becomes idle too long after a Launch request, switch to
|
|
772
|
+
// Unset and NotFound.
|
|
773
|
+
console.warn("requested status is Launch, but workspace pod isn't running yet.");
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
// both requested status and workspace pod status are the same, so stop all polling
|
|
777
|
+
setPollingInterval(WorkspacePollingInterval[WorkspaceStatus.NotFound]);
|
|
778
|
+
dispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.NotFound));
|
|
779
|
+
if (requestedStatus === RequestedWorkspaceStatus.Terminate) {
|
|
780
|
+
// Cleanup termination after terminated
|
|
781
|
+
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Unset));
|
|
782
|
+
}
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
751
785
|
// Check if the workspace is running.
|
|
752
786
|
// If so: need to check workspace idle if set
|
|
753
|
-
// and ensure the pay model is queried
|
|
754
787
|
if (workspaceStatusData.status === HatcheryServiceState.running) {
|
|
755
|
-
// const { idleTimeLimit, lastActivityTime } = workspaceStatusData;
|
|
756
788
|
// in some state other than idle
|
|
757
789
|
if (!idleTimeLimit || idleTimeLimit <= 0) {
|
|
758
790
|
// Workspace is running, but no idle limit set.
|
|
@@ -762,6 +794,7 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
762
794
|
setPollingInterval(WorkspacePollingInterval[WorkspaceStatus.Running]);
|
|
763
795
|
return;
|
|
764
796
|
}
|
|
797
|
+
// handle if idle time is defined
|
|
765
798
|
if (idleTimeLimit && idleTimeLimit > 0 && lastActivityTime && lastActivityTime > 0) {
|
|
766
799
|
const remainingWorkspaceKernelLife = idleTimeLimit - (Date.now() - lastActivityTime);
|
|
767
800
|
if (remainingWorkspaceKernelLife <= 0) {
|
|
@@ -769,7 +802,7 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
769
802
|
// so terminate
|
|
770
803
|
(async ()=>{
|
|
771
804
|
try {
|
|
772
|
-
if (workspaceId) await terminateWorkspace(workspaceId).unwrap();
|
|
805
|
+
if (workspaceId) await terminateWorkspace(encodeURIComponent(workspaceId)).unwrap();
|
|
773
806
|
} catch (error) {
|
|
774
807
|
const errorMessage = error.message || 'Unknown error occurred';
|
|
775
808
|
console.error('Workspace termination failed: ', errorMessage);
|
|
@@ -786,44 +819,48 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
786
819
|
setError('Workspace has been idle for too long. Will shutdown soon');
|
|
787
820
|
}
|
|
788
821
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
822
|
+
}
|
|
823
|
+
// Stopped means the pod is in a failed state — auto-terminate it
|
|
824
|
+
if (workspaceStatusData.status === HatcheryServiceState.stopped) {
|
|
825
|
+
(async ()=>{
|
|
826
|
+
try {
|
|
827
|
+
if (workspaceId) await terminateWorkspace(encodeURIComponent(workspaceId)).unwrap();
|
|
828
|
+
} catch (error) {
|
|
829
|
+
const errorMessage = error.message || 'Unknown error occurred';
|
|
830
|
+
console.error('Workspace termination of stopped pod failed: ', errorMessage);
|
|
831
|
+
notifyUser('Workspace Error', `Failed to terminate stopped workspace: ${errorMessage}`, 2);
|
|
795
832
|
}
|
|
796
|
-
|
|
833
|
+
})();
|
|
834
|
+
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Terminate));
|
|
835
|
+
dispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.Terminating));
|
|
836
|
+
setPollingInterval(WorkspacePollingInterval[WorkspaceStatus.Terminating]);
|
|
837
|
+
notifyUser('Workspace Stopped', 'Workspace entered a failed state and is being terminated', 2);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
if (requestedStatus === RequestedWorkspaceStatus.Launch) {
|
|
841
|
+
// if we have a launch error then requested status has not been met
|
|
842
|
+
if (workspaceQueryStatus === WorkspaceStatus.LaunchError) {
|
|
797
843
|
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Unset));
|
|
844
|
+
dispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.LaunchError));
|
|
845
|
+
return;
|
|
798
846
|
}
|
|
799
|
-
if (
|
|
847
|
+
if (workspaceQueryStatus === WorkspaceStatus.NotFound) {
|
|
848
|
+
return; // pod not yet created, keep waiting
|
|
849
|
+
}
|
|
850
|
+
if (workspaceQueryStatus === WorkspaceStatus.Running) {
|
|
851
|
+
// workspace is running — requested status has been met
|
|
852
|
+
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Unset));
|
|
853
|
+
dispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.Running));
|
|
800
854
|
return;
|
|
801
855
|
}
|
|
856
|
+
// still launching — update active status but keep requestedStatus as Launch
|
|
802
857
|
dispatch(setJEGActiveWorkspaceStatus(workspaceQueryStatus));
|
|
803
|
-
setPollingInterval(WorkspacePollingInterval[workspaceQueryStatus]);
|
|
804
858
|
return;
|
|
805
859
|
}
|
|
806
|
-
if (
|
|
807
|
-
// NotFound means pod is not running
|
|
808
|
-
// either starting up
|
|
809
|
-
// or finally terminated.
|
|
810
|
-
if (requestedStatus === RequestedWorkspaceStatus.Launch) {
|
|
811
|
-
// if the workspace becomes idle too long after a Launch request, switch to
|
|
812
|
-
// Unset and NotFound.
|
|
813
|
-
console.warn("requested status is Launch, but workspace pod isn't running yet.");
|
|
814
|
-
return;
|
|
815
|
-
} else {
|
|
816
|
-
// both requested status and workspace pod status are the same, so stop all polling
|
|
817
|
-
setPollingInterval(WorkspacePollingInterval[WorkspaceStatus.NotFound]);
|
|
818
|
-
dispatch(setJEGActiveWorkspaceStatus(WorkspaceStatus.NotFound));
|
|
819
|
-
if (requestedStatus === RequestedWorkspaceStatus.Terminate) {
|
|
820
|
-
// Cleanup termination after terminated
|
|
821
|
-
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Unset));
|
|
822
|
-
}
|
|
823
|
-
}
|
|
860
|
+
if (requestedStatus === RequestedWorkspaceStatus.Terminate) {
|
|
824
861
|
return;
|
|
825
862
|
}
|
|
826
|
-
// if here, update active workspace status and polling
|
|
863
|
+
// if here, update active workspace status and polling for other states
|
|
827
864
|
dispatch(setJEGActiveWorkspaceStatus(workspaceQueryStatus));
|
|
828
865
|
setPollingInterval(WorkspacePollingInterval[workspaceQueryStatus]);
|
|
829
866
|
}, [
|
|
@@ -833,8 +870,9 @@ const workspaceShutdownAlertLimit = 30000; // 5 minutes: 5 * 60 * 1000 TODO Figu
|
|
|
833
870
|
activeStatus
|
|
834
871
|
]);
|
|
835
872
|
useEffect(()=>{
|
|
873
|
+
// time out if exceeding maximum start timee
|
|
836
874
|
if (requestedStatus === RequestedWorkspaceStatus.Launch && isTimeGreaterThan(requestedStatusTimestamp, MAXIMUM_START_TIME_IN_MINUTES)) {
|
|
837
|
-
if (workspaceId) terminateWorkspace(workspaceId);
|
|
875
|
+
if (workspaceId) terminateWorkspace(encodeURIComponent(workspaceId));
|
|
838
876
|
dispatch(setJEGRequestedWorkspaceStatus(RequestedWorkspaceStatus.Terminate));
|
|
839
877
|
setError('Workspace failed to start. Shutting down');
|
|
840
878
|
}
|
|
@@ -898,21 +936,22 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
898
936
|
coreDispatch
|
|
899
937
|
]);
|
|
900
938
|
/* ── Elapsed timer for launching state ── */ const [elapsedSec, setElapsedSec] = useState(0);
|
|
939
|
+
const interval = useInterval(()=>{
|
|
940
|
+
const elapsed = Math.floor((Date.now() - requestedStatusTimestamp) / 1000);
|
|
941
|
+
setElapsedSec(elapsed);
|
|
942
|
+
}, 1000);
|
|
901
943
|
useEffect(()=>{
|
|
902
|
-
if (status
|
|
944
|
+
if (status === WorkspaceStatus.Launching) {
|
|
945
|
+
interval.start();
|
|
946
|
+
} else {
|
|
903
947
|
setElapsedSec(0);
|
|
904
|
-
|
|
948
|
+
interval.stop();
|
|
905
949
|
}
|
|
906
|
-
|
|
907
|
-
const t = setInterval(()=>{
|
|
908
|
-
setElapsedSec(interval);
|
|
909
|
-
}, 1000);
|
|
910
|
-
return ()=>clearInterval(t);
|
|
950
|
+
return interval.stop;
|
|
911
951
|
}, [
|
|
912
|
-
|
|
913
|
-
|
|
952
|
+
status,
|
|
953
|
+
interval
|
|
914
954
|
]);
|
|
915
|
-
console.log('MicroContainerReduxPanel: status', status);
|
|
916
955
|
/* ── Auto-reset after 15 s in launch-error state ── */ useEffect(()=>{
|
|
917
956
|
if (status !== WorkspaceStatus.LaunchError) return;
|
|
918
957
|
const t = setTimeout(()=>resetStatus(), 6_000);
|
|
@@ -965,9 +1004,6 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
965
1004
|
});
|
|
966
1005
|
}
|
|
967
1006
|
/* ── launching ── */ if (status === WorkspaceStatus.Launching) {
|
|
968
|
-
const mins = Math.floor(elapsedSec / 60);
|
|
969
|
-
const secs = elapsedSec % 60;
|
|
970
|
-
const elapsed = mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
|
|
971
1007
|
return /*#__PURE__*/ jsxs("div", {
|
|
972
1008
|
role: "status",
|
|
973
1009
|
className: PanelStyle$2,
|
|
@@ -990,7 +1026,9 @@ const MicroContainerReduxPanel = ({ compact = false })=>{
|
|
|
990
1026
|
className: "mt-1 text-sm text-base-darker",
|
|
991
1027
|
children: [
|
|
992
1028
|
"Elapsed: ",
|
|
993
|
-
|
|
1029
|
+
elapsedSec,
|
|
1030
|
+
" ",
|
|
1031
|
+
elapsedSec === 1 ? 'second' : 'seconds'
|
|
994
1032
|
]
|
|
995
1033
|
}),
|
|
996
1034
|
/*#__PURE__*/ jsx("p", {
|
|
@@ -1507,6 +1545,8 @@ const jegGatewayApi = GatewayWithTags.injectEndpoints({
|
|
|
1507
1545
|
overrideExisting: true
|
|
1508
1546
|
});
|
|
1509
1547
|
const { useActiveKernelsQuery, useLaunchKernelMutation, useTerminateKernelMutation, useJegGatewayStatusQuery, useReapKernelsMutation } = jegGatewayApi;
|
|
1548
|
+
// select active kernels
|
|
1549
|
+
jegGatewayApi.endpoints.activeKernels.select();
|
|
1510
1550
|
|
|
1511
1551
|
const ActiveKernelInfoPanel = ({ kernelId, kernelName, executionState, uptimeMinutes, onOpenNotebook, forceTerminate = false, rowSpec, connections = 0 })=>{
|
|
1512
1552
|
const [terminateKernel, { isLoading: isTerminatingLoading, isError: isTerminatingError, error: terminatingError }] = useTerminateKernelMutation();
|
|
@@ -1651,6 +1691,8 @@ const PanelStyle$1 = 'min-w-0 rounded-sm border-2 border-base-lighter bg-base-ma
|
|
|
1651
1691
|
const ActiveKernelsPanel = ()=>{
|
|
1652
1692
|
const { data: kernels, isLoading, isError, error } = useActiveKernelsQuery(undefined, {
|
|
1653
1693
|
pollingInterval: 10000,
|
|
1694
|
+
refetchOnMountOrArgChange: 1800,
|
|
1695
|
+
refetchOnFocus: true,
|
|
1654
1696
|
selectFromResult: ({ data, isLoading, isError, error })=>({
|
|
1655
1697
|
data,
|
|
1656
1698
|
isLoading,
|
|
@@ -1949,7 +1991,11 @@ function useGatewayConnectionStatus() {
|
|
|
1949
1991
|
setGatewayMessage(message ?? null);
|
|
1950
1992
|
}, []);
|
|
1951
1993
|
// get the status of the gateway
|
|
1952
|
-
const { data: gatewayStatusData, isFetching: isGatewayFetching, isError } = useJegGatewayStatusQuery(
|
|
1994
|
+
const { data: gatewayStatusData, isFetching: isGatewayFetching, isError } = useJegGatewayStatusQuery(undefined, {
|
|
1995
|
+
pollingInterval: 10000,
|
|
1996
|
+
refetchOnMountOrArgChange: 1800,
|
|
1997
|
+
refetchOnFocus: true
|
|
1998
|
+
});
|
|
1953
1999
|
const gatewayServiceStatus = useMemo(()=>{
|
|
1954
2000
|
if (isGatewayFetching) return 'fetching';
|
|
1955
2001
|
if (!isError) return 'service_unavailable';
|
|
@@ -2194,7 +2240,7 @@ function HorizontalAccordion({ label, expanded, setExpanded, children, expandedW
|
|
|
2194
2240
|
});
|
|
2195
2241
|
const mergedClassNames = mergeDefaultTailwindClassnames(STYLING_DEFAULTS, classNames);
|
|
2196
2242
|
return /*#__PURE__*/ jsx("div", {
|
|
2197
|
-
className:
|
|
2243
|
+
className: `${mergedClassNames.root}`,
|
|
2198
2244
|
style: {
|
|
2199
2245
|
width: expanded ? expandedWidth : collapsedWidth,
|
|
2200
2246
|
transition: `width ${TRANSITION_MS}ms ease`
|
|
@@ -2229,7 +2275,7 @@ function HorizontalAccordion({ label, expanded, setExpanded, children, expandedW
|
|
|
2229
2275
|
]
|
|
2230
2276
|
}),
|
|
2231
2277
|
/*#__PURE__*/ jsx("div", {
|
|
2232
|
-
className: "overflow-hidden
|
|
2278
|
+
className: "overflow-hidden px-2",
|
|
2233
2279
|
style: {
|
|
2234
2280
|
opacity: expanded ? 1 : 0,
|
|
2235
2281
|
pointerEvents: expanded ? 'auto' : 'none',
|
|
@@ -2257,9 +2303,8 @@ const SettingsPanel = ({ showKernels, expanded, setExpanded, width = 380 })=>{
|
|
|
2257
2303
|
setExpanded: setExpanded,
|
|
2258
2304
|
rightSide: true,
|
|
2259
2305
|
expandedWidth: width,
|
|
2260
|
-
children: /*#__PURE__*/ jsxs(
|
|
2261
|
-
|
|
2262
|
-
className: PanelStyle,
|
|
2306
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
2307
|
+
className: `${PanelStyle} overflow-y-scroll`,
|
|
2263
2308
|
children: [
|
|
2264
2309
|
showKernels && /*#__PURE__*/ jsx(KernelLifecyclePanel, {}),
|
|
2265
2310
|
/*#__PURE__*/ jsx(UpgradeActionsPanel, {
|