This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit 8f6b48043e9557edb642a3951ee046de040354f4 Author: Marat Gubaidullin <[email protected]> AuthorDate: Mon Sep 19 17:55:45 2022 -0400 Deployment instead of DeploymentConfig --- karavan-app/pom.xml | 4 + .../camel/karavan/service/KubernetesService.java | 91 ++++++++++++---------- .../camel/karavan/service/StatusService.java | 21 +++++ .../src/main/resources/application.properties | 5 -- karavan-app/src/main/webapp/src/Main.tsx | 14 ++-- .../src/main/webapp/src/projects/ProjectInfo.tsx | 10 +-- .../src/main/webapp/src/projects/ProjectModels.ts | 1 + .../src/main/webapp/src/projects/ProjectPage.tsx | 1 - .../src/main/webapp/src/projects/ProjectsPage.tsx | 13 +++- 9 files changed, 96 insertions(+), 64 deletions(-) diff --git a/karavan-app/pom.xml b/karavan-app/pom.xml index 6957f39..0223375 100644 --- a/karavan-app/pom.xml +++ b/karavan-app/pom.xml @@ -66,6 +66,10 @@ <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-scheduler</artifactId> + </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-smallrye-openapi</artifactId> diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java index efa64f5..e8368d5 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/KubernetesService.java @@ -20,6 +20,7 @@ import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.LogWatch; @@ -35,7 +36,6 @@ import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpec; import io.fabric8.tekton.pipeline.v1beta1.PipelineRunSpecBuilder; import io.fabric8.tekton.pipeline.v1beta1.WorkspaceBindingBuilder; -import io.vertx.core.json.JsonObject; import io.vertx.mutiny.core.eventbus.EventBus; import org.apache.camel.karavan.model.DeploymentStatus; import org.apache.camel.karavan.model.PipelineRunLog; @@ -166,11 +166,7 @@ public class KubernetesService { public void rolloutDeployment(String name, String namespace) { try { - if (kubernetesClient().isAdaptable(OpenShiftClient.class)) { - openshiftClient().deploymentConfigs().inNamespace(namespace).withName(name).deployLatest(); - } else { - // TODO: Implement Deployment for Kubernetes/Minikube - } + kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).rolling(); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } @@ -178,11 +174,7 @@ public class KubernetesService { public void deleteDeployment(String name, String namespace) { try { - if (kubernetesClient().isAdaptable(OpenShiftClient.class)) { - openshiftClient().deploymentConfigs().inNamespace(namespace).withName(name).delete(); - } else { - // TODO: Implement Deployment for Kubernetes/Minikube - } + kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).delete(); } catch (Exception ex) { LOGGER.error(ex.getMessage()); } @@ -198,43 +190,58 @@ public class KubernetesService { public DeploymentStatus getDeploymentStatus(String name, String namespace) { try { - if (kubernetesClient().isAdaptable(OpenShiftClient.class)) { - DeploymentConfig dc = openshiftClient().deploymentConfigs().inNamespace(namespace).withName(name).get(); - String dsImage = dc.getSpec().getTemplate().getSpec().getContainers().get(0).getImage(); - String imageName = dsImage.startsWith("image-registry.openshift-image-registry.svc") - ? dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "") - : dsImage; - - List<Pod> pods = openshiftClient().pods().inNamespace(namespace) - .withLabel("app.kubernetes.io/name", name) - .withLabel("deploymentconfig", name) - .list().getItems(); - - List<PodStatus> podStatuses = pods.stream().map(pod -> new PodStatus( - pod.getMetadata().getName(), - pod.getStatus().getContainerStatuses().get(0).getStarted(), - pod.getStatus().getContainerStatuses().get(0).getReady(), - getPodReason(pod), - pod.getMetadata().getLabels().get("deployment") - )).collect(Collectors.toList()); - - return new DeploymentStatus( - imageName, - dc.getSpec().getReplicas(), - dc.getStatus().getReadyReplicas(), - dc.getStatus().getUnavailableReplicas(), - podStatuses - ); - } else { - // TODO: Implement Deployment for Kubernetes/Minikube - return new DeploymentStatus(); - } + Deployment deployment = kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).get(); + String dsImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage(); + String imageName = dsImage.startsWith("image-registry.openshift-image-registry.svc") + ? dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "") + : dsImage; + + List<Pod> pods = openshiftClient().pods().inNamespace(namespace) + .withLabel("app.kubernetes.io/name", name) + .withLabel("app.openshift.io/runtime", "camel") + .list().getItems(); + + List<PodStatus> podStatuses = pods.stream().map(pod -> new PodStatus( + pod.getMetadata().getName(), + pod.getStatus().getContainerStatuses().get(0).getStarted(), + pod.getStatus().getContainerStatuses().get(0).getReady(), + getPodReason(pod), + pod.getMetadata().getLabels().get("app.kubernetes.io/name") + )).collect(Collectors.toList()); + + return new DeploymentStatus( + imageName, + deployment.getSpec().getReplicas(), + deployment.getStatus().getReadyReplicas(), + deployment.getStatus().getUnavailableReplicas(), + podStatuses + ); } catch (Exception ex) { LOGGER.error(ex.getMessage()); return new DeploymentStatus(); } } + public boolean hasDeployment(String name, String namespace) { + try { + Deployment deployment = kubernetesClient().apps().deployments().inNamespace(namespace).withName(name).get(); + return deployment != null; + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return false; + } + } + + public List<String> getCamelDeployments(String namespace) { + try { + return kubernetesClient().apps().deployments().inNamespace(namespace).withLabel("app.openshift.io/runtime","camel").list().getItems() + .stream().map(deployment -> deployment.getMetadata().getName()).collect(Collectors.toList()); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return List.of(); + } + } + private String getPodReason (Pod pod){ try { return pod.getStatus().getContainerStatuses().get(0).getState().getWaiting().getReason(); diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java index 0d340c6..eb2e33b 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/StatusService.java @@ -18,6 +18,7 @@ package org.apache.camel.karavan.service; import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; import io.quarkus.runtime.configuration.ProfileManager; +import io.quarkus.scheduler.Scheduled; import io.quarkus.vertx.ConsumeEvent; import io.smallrye.mutiny.tuples.Tuple4; import io.vertx.core.json.JsonObject; @@ -70,6 +71,26 @@ public class StatusService { return webClient; } + @Scheduled(every="10s") + void checkDeployedProjects() { + LOGGER.info("Check deployed projects"); + infinispanService.getProjects().forEach(project -> { + Optional<KaravanConfiguration.Environment> env = configuration.environments().stream().filter(environment -> environment.name().equals("dev")).findFirst(); + if (env.isPresent()) { + boolean hasDeployment = kubernetesService.hasDeployment(project.getProjectId(), env.get().namespace()); + System.out.println("Project " + project.getName() + " deployed: " + project.getDeployed() + " hasDeployment: " + hasDeployment); + if (!project.getDeployed() && hasDeployment) { + project.setDeployed(true); + infinispanService.saveProject(project); + } else if (project.getDeployed() && !hasDeployment) { + project.setDeployed(false); + infinispanService.saveProject(project); + infinispanService.saveProjectStatus(new ProjectStatus(project.getProjectId(), List.of(), System.currentTimeMillis())); + } + } + }); + } + @ConsumeEvent(value = CMD_COLLECT_STATUSES, blocking = true, ordered = true) public void collectStatuses(String projectId) throws Exception { if ((System.currentTimeMillis() - lastCollect) > configuration.statusThreshold()) { diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties index 817a4ec..c58c496 100644 --- a/karavan-app/src/main/resources/application.properties +++ b/karavan-app/src/main/resources/application.properties @@ -40,11 +40,6 @@ karavan.config.environments[2].pipeline=karavan-quarkus karavan.config.environments[2].cluster=svc.cluster.local karavan.config.environments[2].active=false -%dev.karavan.config.environments[0].cluster=apps.cluster-bzs7w.bzs7w.sandbox863.opentlc.com -%dev.karavan.config.environments[1].cluster=apps.cluster-bzs7w.bzs7w.sandbox863.opentlc.com -%dev.karavan.config.environments[2].cluster=apps.cluster-bzs7w.bzs7w.sandbox863.opentlc.com - - # Infinispan Server address #quarkus.infinispan-client.server-list=localhost:12345 quarkus.infinispan-client.devservices.enabled=false diff --git a/karavan-app/src/main/webapp/src/Main.tsx b/karavan-app/src/main/webapp/src/Main.tsx index 00e3efa..aa7341a 100644 --- a/karavan-app/src/main/webapp/src/Main.tsx +++ b/karavan-app/src/main/webapp/src/Main.tsx @@ -127,9 +127,6 @@ export class Main extends React.Component<Props, State> { } getData() { - KaravanApi.getConfiguration((config: any) => { - this.setState({ config: config }) - }); KaravanApi.getKameletNames(names => names.forEach(name => { KaravanApi.getKamelet(name, yaml => KameletApi.saveKamelet(yaml)) })); @@ -247,11 +244,14 @@ export class Main extends React.Component<Props, State> { }; onGetProjects = () => { - KaravanApi.getProjects((projects: Project[]) => { - this.setState({ - projects: projects, request: uuidv4() - }) + KaravanApi.getConfiguration((config: any) => { + KaravanApi.getProjects((projects: Project[]) => { + this.setState({ + projects: projects, request: uuidv4(), config: config + }) + }); }); + } getMain() { diff --git a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx index 25e6cf8..b2bec98 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectInfo.tsx @@ -59,7 +59,7 @@ export class ProjectInfo extends React.Component<Props, State> { componentDidMount() { this.onRefresh(); - this.interval = setInterval(() => this.onRefreshStatus(), 700); + this.interval = setInterval(() => this.onRefreshStatus(), 1300); } componentWillUnmount() { @@ -146,16 +146,14 @@ export class ProjectInfo extends React.Component<Props, State> { const status = this.state.status?.statuses.find(s => s.environment === env) const pipelineResult = status?.lastPipelineRunResult; const isRunning = pipelineResult === 'Running'; - return (<Tooltip content="Commit, push, build and deploy" position={"left"}> + return (<Tooltip content="Build and deploy" position={"left"}> <Button isLoading={isDeploying ? true : undefined} isDisabled={isDeploying || isRunning || isPushing} isSmall variant="secondary" className="project-button" icon={!isDeploying ? <BuildIcon/> : <div></div>} - onClick={e => { - this.push(() => this.build()); - }}> + onClick={e => this.build()}> {isDeploying ? "..." : "Deploy"} </Button> </Tooltip>) @@ -205,7 +203,7 @@ export class ProjectInfo extends React.Component<Props, State> { } getEnvPanel(env: string) { - const {status} = this.state; + const {status, project} = this.state; const deploymentStatus = status?.statuses.find(s => s.environment === env)?.deploymentStatus; return ( <DescriptionList isHorizontal> diff --git a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts b/karavan-app/src/main/webapp/src/projects/ProjectModels.ts index 172e90a..2d98870 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectModels.ts +++ b/karavan-app/src/main/webapp/src/projects/ProjectModels.ts @@ -3,6 +3,7 @@ export class Project { name: string = ''; description: string = ''; lastCommit: string = ''; + deployed: boolean = false; public constructor(projectId: string, name: string, description: string, lastCommit: string); public constructor(init?: Partial<Project>); diff --git a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx index 1758ac2..ce45398 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectPage.tsx @@ -46,7 +46,6 @@ import {UploadModal} from "./UploadModal"; import {ProjectInfo} from "./ProjectInfo"; import {ProjectDashboard} from "./ProjectDashboard"; import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml"; -import {CamelUtil} from "karavan-core/src/core/api/CamelUtil"; import * as yaml from 'js-yaml'; interface Props { diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx index cad4f3d..24c72ed 100644 --- a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx +++ b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx @@ -14,8 +14,6 @@ import { Form, Badge, Tooltip, - ToggleGroup, - ToggleGroupItem, Bullseye, EmptyState, EmptyStateVariant, @@ -69,6 +67,15 @@ export class ProjectsPage extends React.Component<Props, State> { description: '', projectId: '', }; + interval: any; + + componentDidMount() { + this.interval = setInterval(() => this.props.onRefresh.call(this), 500); + } + + componentWillUnmount() { + clearInterval(this.interval); + } tools = () => (<Toolbar id="toolbar-group-types"> <ToolbarContent> @@ -193,7 +200,7 @@ export class ProjectsPage extends React.Component<Props, State> { <Td noPadding style={{width:"180px"}}> <Flex direction={{default: "row"}}> {environments.filter(e => e !== undefined) - .map(e => <FlexItem key={e}><Badge isRead>{e}</Badge></FlexItem>)} + .map(e => <FlexItem key={e}><Badge isRead={!project.deployed}>{e}</Badge></FlexItem>)} </Flex> </Td> <Td isActionCell>
