Skip to content

Commit

Permalink
Improved container management ui
Browse files Browse the repository at this point in the history
  • Loading branch information
mgubaidullin committed May 14, 2024
1 parent 11a74ad commit d875aaf
Show file tree
Hide file tree
Showing 15 changed files with 58 additions and 158 deletions.
2 changes: 1 addition & 1 deletion karavan-app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<camel-kamelet.version>4.5.0</camel-kamelet.version>
<surefire-plugin.version>3.1.0</surefire-plugin.version>
<jgit.version>3.1.0</jgit.version>
<quinoa.version>2.3.6</quinoa.version>
<quinoa.version>2.3.7</quinoa.version>
<resources-plugin.version>3.3.0</resources-plugin.version>
<docker-java.version>3.3.5</docker-java.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ public List<ContainerStatus> getContainerStatusesByEnv(@PathParam("env") String
@Path("/{projectId}/{env}")
public List<ContainerStatus> getContainerStatusesByProjectAndEnv(@PathParam("projectId") String projectId, @PathParam("env") String env) throws Exception {
return karavanCacheService.getContainerStatuses(projectId, env).stream()
.filter(podStatus -> Objects.equals(podStatus.getType(), ContainerStatus.ContainerType.project))
.sorted(Comparator.comparing(ContainerStatus::getContainerName))
.collect(Collectors.toList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,17 @@ void cleanContainersStatuses(List<ContainerStatus> statusesInDocker) {
.filter(cs -> !namesInDocker.contains(cs.getContainerName()))
.forEach(containerStatus -> {
eventBus.publish(ContainerStatusService.CONTAINER_DELETED, JsonObject.mapFrom(containerStatus));
karavanCacheService.deleteContainerStatus(containerStatus);
karavanCacheService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv());
});
}
}

@ConsumeEvent(value = CONTAINER_DELETED, blocking = true, ordered = true)
public void cleanContainersStatus(JsonObject data) {
ContainerStatus containerStatus = data.mapTo(ContainerStatus.class);
karavanCacheService.deleteContainerStatus(containerStatus);
karavanCacheService.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv());
}

private boolean checkTransit(ContainerStatus cs) {
if (cs.getContainerId() == null && cs.getInTransit()) {
return Instant.parse(cs.getInitDate()).until(Instant.now(), ChronoUnit.SECONDS) < 10;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class Constants {
public static final String NOTIFICATION_HEADER_CLASS_NAME = "className";

public static final String NOTIFICATION_EVENT_COMMIT = "commit";
public static final String NOTIFICATION_EVENT_CONTAINER_STATUS = "container_status";

public enum CamelRuntime {
CAMEL_MAIN("camel-main"),
Expand Down
9 changes: 9 additions & 0 deletions karavan-app/src/main/webui/src/api/KaravanApi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,15 @@ export class KaravanApi {
});
}

static async getContainerStatus(projectId: string, env: string, after: (res: AxiosResponse<ContainerStatus[]>) => void) {
instance.get('/ui/container/' + projectId + "/" + env)
.then(res => {
after(res);
}).catch(err => {
after(err);
});
}

static async getAllDeploymentStatuses(after: (statuses: DeploymentStatus[]) => void) {
instance.get('/ui/infrastructure/deployment')
.then(res => {
Expand Down
4 changes: 1 addition & 3 deletions karavan-app/src/main/webui/src/api/NotificationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
*/

import {Subject} from "rxjs";
import {ProjectEventBus} from "./ProjectEventBus";
import {unstable_batchedUpdates} from "react-dom";
import {useLogStore, useProjectStore} from "./ProjectStore";
import {useProjectStore} from "./ProjectStore";
import {ProjectService} from "./ProjectService";

export class KaravanEvent {
Expand All @@ -42,7 +41,6 @@ export const NotificationEventBus = {

console.log("Start Notification subscriber");
const sub = NotificationEventBus.onEvent()?.subscribe((event: KaravanEvent) => {
// console.log('KaravanEvent', event);
if (event.event === 'commit' && event.className === 'Project') {
const projectId = event.data?.projectId;
if (useProjectStore.getState().project?.projectId === projectId) {
Expand Down
17 changes: 17 additions & 0 deletions karavan-app/src/main/webui/src/api/ProjectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,23 @@ export class ProjectService {
});
}

public static refreshContainerStatus(projectId: string, env: string) {
KaravanApi.getContainerStatus(projectId, env, (res) => {
if (res.status === 200) {
const oldContainers = useStatusesStore.getState().containers;
const newContainers = res.data;
const newMap = new Map<string, ContainerStatus>(
newContainers.map(container => [container.containerName, container])
);
const containers = oldContainers
.filter(container => newMap.has(container.containerName)) // Filter out old containers not in new
.map(container => newMap.get(container.containerName)!) // Replace with new containers
.concat(newContainers.filter(container => !oldContainers.some(old => old.containerName === container.containerName)));
useStatusesStore.setState({containers: containers});
}
})
}

public static refreshAllServicesStatuses() {
KaravanApi.getAllServiceStatuses((statuses: ServiceStatus[]) => {
useStatusesStore.setState({services: statuses});
Expand Down
1 change: 0 additions & 1 deletion karavan-app/src/main/webui/src/editor/EditorToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import '../designer/karavan.css';
import {useAppConfigStore, useFileStore, useProjectStore} from "../api/ProjectStore";
import {shallow} from "zustand/shallow";
import {DevModeToolbar} from "../project/DevModeToolbar";
import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
import {KaravanApi} from "../api/KaravanApi";
import {EventBus} from "../designer/utils/EventBus";
import UpdateIcon from "@patternfly/react-icons/dist/esm/icons/cog-icon";
Expand Down
10 changes: 1 addition & 9 deletions karavan-app/src/main/webui/src/main/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

import React, {useEffect, useRef} from "react";
import React, {useEffect} from "react";
import {KaravanApi} from "../api/KaravanApi";
import {
Flex,
Expand Down Expand Up @@ -74,14 +74,6 @@ export function Main() {
});
}

function showSpinner() {
return KaravanApi.authType === undefined || readiness === undefined;
}

function showStepper() {
return readiness !== undefined && readiness.status !== true;
}

function showMain() {
return KaravanApi.authType !== undefined && readiness?.status === true && isAuthorized;
}
Expand Down
24 changes: 3 additions & 21 deletions karavan-app/src/main/webui/src/main/useMainHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
*/

import {KaravanApi} from "../api/KaravanApi";
import {KameletApi} from "karavan-core/lib/api/KameletApi";
import '../designer/karavan.css';
import {ComponentApi} from "karavan-core/lib/api/ComponentApi";
import {AppConfig, ContainerStatus, Project} from "../api/ProjectModels";
import {useAppConfigStore, useProjectsStore, useStatusesStore} from "../api/ProjectStore";
import {AppConfig, Project} from "../api/ProjectModels";
import {useAppConfigStore, useProjectsStore} from "../api/ProjectStore";
import {InfrastructureAPI} from "../designer/utils/InfrastructureAPI";
import {shallow} from "zustand/shallow";
import {ProjectService} from "../api/ProjectService";
Expand All @@ -29,17 +28,8 @@ export function useMainHook () {

const [setConfig, isAuthorized] = useAppConfigStore((s) => [s.setConfig, s.isAuthorized], shallow)
const [setProjects] = useProjectsStore((s) => [s.setProjects], shallow)
const [setContainers] = useStatusesStore((state) => [state.setContainers], shallow);
const [selectedEnv, selectEnvironment] = useAppConfigStore((state) => [state.selectedEnv, state.selectEnvironment], shallow)

const getStatuses = () => {
if (isAuthorized || KaravanApi.authType === 'public') {
KaravanApi.getAllContainerStatuses((statuses: ContainerStatus[]) => {
setContainers(statuses);
});
}
}

const getData = () => {
if (isAuthorized) {
KaravanApi.getConfiguration((config: AppConfig) => {
Expand Down Expand Up @@ -70,13 +60,5 @@ export function useMainHook () {
});
}

async function updateSupportedComponents(): Promise<void> {
await new Promise(resolve => {
KaravanApi.getSupportedComponents(jsons => {
ComponentApi.saveSupportedComponents(jsons);
})
});
}

return { getData, getStatuses }
return { getData }
}
64 changes: 0 additions & 64 deletions karavan-app/src/main/webui/src/project/BuildToolbar.tsx

This file was deleted.

49 changes: 12 additions & 37 deletions karavan-app/src/main/webui/src/project/DevModeToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,18 @@
*/

import React, {useEffect, useState} from 'react';
import {Badge, Button, Flex, FlexItem, Label, Spinner, Switch, Tooltip, TooltipPosition} from '@patternfly/react-core';
import {Badge, Button, Flex, FlexItem, Label, Switch, Tooltip, TooltipPosition} from '@patternfly/react-core';
import '../designer/karavan.css';
import RocketIcon from "@patternfly/react-icons/dist/esm/icons/rocket-icon";
import ReloadIcon from "@patternfly/react-icons/dist/esm/icons/bolt-icon";
import DeleteIcon from "@patternfly/react-icons/dist/esm/icons/trash-icon";
import {useAppConfigStore, useDevModeStore, useLogStore, useProjectStore, useStatusesStore} from "../api/ProjectStore";
import {useAppConfigStore, useLogStore, useProjectStore, useStatusesStore} from "../api/ProjectStore";
import {ProjectService} from "../api/ProjectService";
import {shallow} from "zustand/shallow";
import UpIcon from "@patternfly/react-icons/dist/esm/icons/running-icon";
import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon";
import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon";
import StopIcon from "@patternfly/react-icons/dist/esm/icons/stop-icon";
import {ContainerStatus} from "../api/ProjectModels";
import "./devmode.css"
import "./DevModeToolbar.css"

interface Props {
reloadOnly?: boolean
Expand All @@ -38,19 +36,16 @@ interface Props {
export function DevModeToolbar(props: Props) {

const [config] = useAppConfigStore((state) => [state.config], shallow)
const [status] = useDevModeStore((state) => [state.status], shallow)
const [project, refreshTrace] = useProjectStore((state) => [state.project, state.refreshTrace], shallow)
const [containers] = useStatusesStore((state) => [state.containers], shallow);
const [verbose, setVerbose] = useState(false);
const [setShowLog] = useLogStore((s) => [s.setShowLog], shallow);
const [poll, setPoll] = useState(false);
const [currentContainerStatus, setCurrentContainerStatus] = useState<ContainerStatus>();

const containerStatus = containers.filter(c => c.containerName === project.projectId).at(0);
const commands = containerStatus?.commands || ['run'];
const isRunning = containerStatus?.state === 'running';
const inTransit = containerStatus?.inTransit;
const isLoading = status === 'wip';
const color = containerStatus?.state === 'running' ? "green" : "grey";
const icon = isRunning ? <UpIcon/> : <DownIcon/>;
const inDevMode = containerStatus?.type === 'devmode';
Expand All @@ -60,39 +55,19 @@ export function DevModeToolbar(props: Props) {
refreshContainer();
}, 1300)
return () => clearInterval(interval);
}, [poll, currentContainerStatus, containers]);
}, [currentContainerStatus, containers]);

function refreshContainer(){
if (poll) {
ProjectService.refreshAllContainerStatuses();
ProjectService.refreshCamelStatus(project.projectId, config.environment);
ProjectService.refreshImages(project.projectId);
if (refreshTrace) {
ProjectService.refreshCamelTraces(project.projectId, config.environment);
}
if (currentContainerStatus && !containerStatus) {
setPoll(false);
}
setCurrentContainerStatus(containerStatus);
ProjectService.refreshContainerStatus(project.projectId, config.environment);
ProjectService.refreshCamelStatus(project.projectId, config.environment);
ProjectService.refreshImages(project.projectId);
if (refreshTrace) {
ProjectService.refreshCamelTraces(project.projectId, config.environment);
}
setCurrentContainerStatus(containerStatus);
}

return (<Flex className="toolbar" direction={{default: "row"}} alignItems={{default: "alignItemsCenter"}}>
<FlexItem className="dev-action-button-place refresher">
{poll && <Spinner className="spinner" size="lg" aria-label="Refresh"/>}
<Tooltip content={poll ? "Stop refresh" : "Refresh auto"} position={TooltipPosition.bottom}>
<Button className="dev-action-button button"
icon={poll ? <StopIcon/> : <RefreshIcon/>}
variant={"link"}
onClick={e => setPoll(!poll)}/>
</Tooltip>
</FlexItem>
{/*Replace with something else because Spinner is used fo refresh*/}
{/*{(inTransit || isLoading) &&*/}
{/* <FlexItem>*/}
{/* <Spinner size="lg" aria-label="spinner"/>*/}
{/* </FlexItem>*/}
{/*}*/}
{containerStatus?.containerId && <FlexItem>
<Label icon={icon} color={color}>
<Tooltip content={"Show log"} position={TooltipPosition.bottom}>
Expand Down Expand Up @@ -122,7 +97,7 @@ export function DevModeToolbar(props: Props) {
icon={<RocketIcon/>}
onClick={() => {
ProjectService.startDevModeContainer(project, verbose);
setPoll(true);
// setPoll(true);
}}>
{"Run"}
</Button>
Expand All @@ -145,7 +120,7 @@ export function DevModeToolbar(props: Props) {
variant={"control"}
icon={<DeleteIcon/>}
onClick={() => {
setPoll(true);
// setPoll(true);
ProjectService.deleteDevModeContainer(project);
}}>
</Button>
Expand Down
7 changes: 3 additions & 4 deletions karavan-app/src/main/webui/src/project/ProjectPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import '../designer/karavan.css';
import {ProjectToolbar} from "./ProjectToolbar";
import {ProjectLogPanel} from "./log/ProjectLogPanel";
import {Project, ProjectType} from "../api/ProjectModels";
import {useAppConfigStore, useFilesStore, useFileStore, useProjectsStore, useProjectStore} from "../api/ProjectStore";
import {useFilesStore, useFileStore, useProjectsStore, useProjectStore} from "../api/ProjectStore";
import {MainToolbar} from "../designer/MainToolbar";
import {ProjectTitle} from "./ProjectTitle";
import {ProjectPanel} from "./ProjectPanel";
Expand All @@ -41,8 +41,8 @@ export function ProjectPage() {
const {file, operation} = useFileStore();
const [files] = useFilesStore((s) => [s.files], shallow);
const [projects] = useProjectsStore((state) => [state.projects], shallow)
const [project, setProject, tabIndex, setTabIndex, refreshTrace] =
useProjectStore((s) => [s.project, s.setProject, s.tabIndex, s.setTabIndex, s.refreshTrace], shallow);
const [project, setProject, tabIndex, setTabIndex] =
useProjectStore((s) => [s.project, s.setProject, s.tabIndex, s.setTabIndex], shallow);

let {projectId} = useParams();

Expand All @@ -61,7 +61,6 @@ export function ProjectPage() {
useEffect(() => {
const interval = setInterval(() => {
if (tabIndex === 'build' || tabIndex === 'container') {
ProjectService.refreshAllContainerStatuses();
ProjectService.refreshAllDeploymentStatuses();
ProjectService.refreshImages(project.projectId);
}
Expand Down

0 comments on commit d875aaf

Please sign in to comment.