You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2022/06/29 01:27:58 UTC

[camel-karavan] branch main updated: Fix issue with pipeline (#400)

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


The following commit(s) were added to refs/heads/main by this push:
     new 8625fe0  Fix issue with pipeline (#400)
8625fe0 is described below

commit 8625fe0de74adca6e7ff795a8906010b10b2e464
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Tue Jun 28 21:27:54 2022 -0400

    Fix issue with pipeline (#400)
---
 karavan-app/pom.xml                                |   4 +
 .../camel/karavan/service/KubernetesService.java   |  18 +++
 .../camel/karavan/service/StatusService.java       |   2 +
 .../src/main/resources/application.properties      |   2 +-
 .../src/main/webapp/src/builder/BuilderPage.tsx    |   2 +-
 .../main/webapp/src/projects/ProjectDashboard.tsx  |  12 +-
 .../src/main/webapp/src/projects/ProjectHeader.tsx |  27 ++--
 .../src/main/webapp/src/projects/ProjectsPage.tsx  |   5 +-
 .../main/webapp/src/projects/PropertiesEditor.tsx  |   2 +-
 .../main/webapp/src/projects/PropertiesTable.tsx   | 148 +++++++++++++++++++++
 .../openshift/karavan-quarkus-task.yaml            |  15 ++-
 11 files changed, 211 insertions(+), 26 deletions(-)

diff --git a/karavan-app/pom.xml b/karavan-app/pom.xml
index 3457950..64b0c30 100644
--- a/karavan-app/pom.xml
+++ b/karavan-app/pom.xml
@@ -83,6 +83,10 @@
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-kubernetes-client</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-openshift-client</artifactId>
+        </dependency>
         <dependency>
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-smallrye-health</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 71401df..d48e1a7 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
@@ -21,6 +21,8 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
 import io.fabric8.kubernetes.api.model.Secret;
 import io.fabric8.kubernetes.client.DefaultKubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.openshift.api.model.DeploymentConfig;
+import io.fabric8.openshift.client.OpenShiftClient;
 import io.fabric8.tekton.client.DefaultTektonClient;
 import io.fabric8.tekton.pipeline.v1beta1.ParamBuilder;
 import io.fabric8.tekton.pipeline.v1beta1.PipelineRef;
@@ -54,6 +56,11 @@ public class KubernetesService {
         return new DefaultTektonClient(kubernetesClient());
     }
 
+    @Produces
+    public OpenShiftClient openshiftClient() {
+        return kubernetesClient().adapt(OpenShiftClient.class);
+    }
+
     private static final Logger LOGGER = Logger.getLogger(KubernetesService.class.getName());
 
     public String createPipelineRun(Project project, String namespace) throws Exception {
@@ -91,6 +98,17 @@ public class KubernetesService {
         return tektonClient().v1beta1().pipelineRuns().inNamespace(namespace).withName(name).get();
     }
 
+    public String getReplicas(String name, String namespace)  {
+        if (kubernetesClient().isAdaptable(OpenShiftClient.class)) {
+            DeploymentConfig dc = openshiftClient().deploymentConfigs().inNamespace(namespace).withName(name).get();
+            dc.getSpec().getReplicas();
+            dc.getStatus().getReadyReplicas();
+        } else {
+//            throw new Exception("Adapting to OpenShiftClient not support. Check if adapter is present, and that env provides /oapi root path.");
+        }
+        return "";
+    }
+
     public Secret getKaravanSecret() {
         return kubernetesClient().secrets().inNamespace(namespace).withName("karavan").get();
     }
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 074feb8..3742873 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
@@ -69,6 +69,7 @@ public class StatusService {
         LOGGER.info("Event received to collect statuses for the project " + projectId);
         if (System.currentTimeMillis() - lastCollect > configuration.statusThreshold()){
             getStatuses(projectId);
+            lastCollect = System.currentTimeMillis();
         }
     }
 
@@ -89,6 +90,7 @@ public class StatusService {
 
         Project project = infinispanService.getProject(projectId);
         Tuple2<Boolean, String> p = getProjectPipelineStatus(project.getLastPipelineRun());
+        System.out.println(p.getItem2());
         if (p.getItem1()) status.setPipeline(p.getItem2());
 
         LOGGER.info("Storing status in cache for " + projectId);
diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties
index b8ff145..421b93d 100644
--- a/karavan-app/src/main/resources/application.properties
+++ b/karavan-app/src/main/resources/application.properties
@@ -13,7 +13,7 @@ karavan.config.group-id=org.camel.karavan.demo
 karavan.config.image-group=karavan
 karavan.config.runtime=QUARKUS
 karavan.config.runtime-version=2.10.0.Final
-karavan.config.status-threshold=5000
+karavan.config.status-threshold=1000
 karavan.config.environments[0].name=dev
 karavan.config.environments[0].namespace=karavan
 karavan.config.environments[0].cluster=svc.cluster.local
diff --git a/karavan-app/src/main/webapp/src/builder/BuilderPage.tsx b/karavan-app/src/main/webapp/src/builder/BuilderPage.tsx
index 605521c..803c9c4 100644
--- a/karavan-app/src/main/webapp/src/builder/BuilderPage.tsx
+++ b/karavan-app/src/main/webapp/src/builder/BuilderPage.tsx
@@ -53,7 +53,7 @@ import ProjectIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon';
 import ClipboardIcon from '@patternfly/react-icons/dist/esm/icons/clipboard-icon';
 import RunIcon from '@patternfly/react-icons/dist/esm/icons/play-circle-icon';
 import {ProjectModel, StepStatus} from "karavan-core/lib/model/ProjectModel";
-import {PropertiesTable} from "./PropertiesTable";
+import {PropertiesTable} from "../projects/PropertiesTable";
 
 interface Props {
     project: ProjectModel,
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx
index 203bd6f..a49f2bb 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectDashboard.tsx
@@ -118,14 +118,14 @@ export class ProjectDashboard extends React.Component<Props, State> {
 
     getEnvironmentData() {
         const used  = true;
-        const specReplicas = 3;
-        const data = Array.from({length: specReplicas}, (v, k) => {
-            return { x: k, y: 100/specReplicas }
+        const replicas = 3;
+        const data = Array.from({length: replicas}, (v, k) => {
+            return { x: k, y: 100/replicas }
         });
-        const statusReplicas = 2;
-        const colorScale = Array.from({length: specReplicas}, (v, k) => {
+        const readyReplicas = 2;
+        const colorScale = Array.from({length: replicas}, (v, k) => {
             console.log(" " + k)
-            if (k < statusReplicas) return "rgb(56, 129, 47)"
+            if (k < readyReplicas) return "rgb(56, 129, 47)"
             else return "#8bc1f7"
         })
         return (
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx b/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx
index 85ed5d2..efb4e2a 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectHeader.tsx
@@ -49,7 +49,7 @@ export class ProjectHeader extends React.Component<Props, State> {
 
     componentDidMount() {
         this.onRefresh();
-        this.interval = setInterval(() => this.onRefreshStatus(), 3000);
+        this.interval = setInterval(() => this.onRefreshStatus(), 700);
     }
 
     componentWillUnmount() {
@@ -152,18 +152,21 @@ export class ProjectHeader extends React.Component<Props, State> {
         const isSucceeded = status?.pipeline === 'Succeeded';
         let classname = "pipeline"
         if (isRunning) classname = classname + " pipeline-running";
-        if (isFailed) classname = classname + " pipeline-running";
+        if (isFailed) classname = classname + " pipeline-failed";
         if (isSucceeded) classname = classname + " pipeline-succeeded";
         return (
-            <Flex spaceItems={{default: 'spaceItemsNone'}} className={classname} direction={{default: "row"}}
-                  alignItems={{default: "alignItemsCenter"}}>
-                <FlexItem style={{height: "18px"}}>
-                    {isRunning && <Spinner isSVG diameter="16px"/>}
-                </FlexItem>
-                <FlexItem style={{height: "18px"}}>
-                    {project?.lastPipelineRun ? project?.lastPipelineRun : "-"}
-                </FlexItem>
-            </Flex>
+            <Tooltip content={status?.pipeline} position={"right"}>
+                <Flex spaceItems={{default: 'spaceItemsNone'}} className={classname} direction={{default: "row"}}
+                      alignItems={{default: "alignItemsCenter"}}>
+                    <FlexItem style={{height: "18px"}}>
+                        {isRunning && <Spinner isSVG diameter="16px"/>}
+                    </FlexItem>
+                    <FlexItem style={{height: "18px"}}>
+                        {project?.lastPipelineRun ? project?.lastPipelineRun : "-"}
+                    </FlexItem>
+                </Flex>
+            </Tooltip>
+
         )
     }
 
@@ -203,7 +206,7 @@ export class ProjectHeader extends React.Component<Props, State> {
                                 <DescriptionListGroup>
                                     <DescriptionListTerm>Commit</DescriptionListTerm>
                                     <DescriptionListDescription>
-                                        <Tooltip content={project?.lastCommit} position={"bottom"}>
+                                        <Tooltip content={project?.lastCommit} position={"right"}>
                                             <Badge>{project?.lastCommit ? project?.lastCommit?.substr(0, 7) : "-"}</Badge>
                                         </Tooltip>
                                     </DescriptionListDescription>
diff --git a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
index 836e54a..2e348d3 100644
--- a/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
+++ b/karavan-app/src/main/webapp/src/projects/ProjectsPage.tsx
@@ -147,6 +147,7 @@ export class ProjectsPage extends React.Component<Props, State> {
     }
 
     render() {
+        const runtime = this.props.config?.runtime ? this.props.config.runtime : "QUARKUS";
         const projects = this.state.projects.filter(p => p.name.includes(this.state.filter) || p.description.includes(this.state.filter));
         const environments: string[] = this.props.config.environments && Array.isArray(this.props.config.environments)
             ? Array.from(this.props.config.environments)
@@ -173,8 +174,8 @@ export class ProjectsPage extends React.Component<Props, State> {
                             {projects.map(project => (
                                 <Tr key={project.projectId}>
                                     <Td modifier={"fitContent"}>
-                                        <Tooltip content={this.props.config.runtime} position={"left"}>
-                                            <Badge className="runtime-badge">{this.props.config.runtime.substring(0,1)}</Badge>
+                                        <Tooltip content={runtime} position={"left"}>
+                                            <Badge className="runtime-badge">{runtime.substring(0,1)}</Badge>
                                         </Tooltip>
                                     </Td>
                                     <Td>
diff --git a/karavan-app/src/main/webapp/src/projects/PropertiesEditor.tsx b/karavan-app/src/main/webapp/src/projects/PropertiesEditor.tsx
index bac7763..592350c 100644
--- a/karavan-app/src/main/webapp/src/projects/PropertiesEditor.tsx
+++ b/karavan-app/src/main/webapp/src/projects/PropertiesEditor.tsx
@@ -4,7 +4,7 @@ import {
 } from '@patternfly/react-core';
 import '../designer/karavan.css';
 import {Project, ProjectFile, ProjectFileTypes} from "../models/ProjectModels";
-import {PropertiesTable} from "../builder/PropertiesTable";
+import {PropertiesTable} from "./PropertiesTable";
 import {ProjectModel} from "karavan-core/lib/model/ProjectModel";
 import {ProjectModelApi} from "karavan-core/lib/api/ProjectModelApi";
 
diff --git a/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx b/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx
new file mode 100644
index 0000000..e3f5958
--- /dev/null
+++ b/karavan-app/src/main/webapp/src/projects/PropertiesTable.tsx
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button, Flex, FlexItem,
+    Modal,
+    PageSection,
+    Panel,
+    PanelMain,
+    PanelMainBody,
+    TextInput
+} from '@patternfly/react-core';
+import '../designer/karavan.css';
+import {TableComposable, Tbody, Td, Th, Thead, Tr} from "@patternfly/react-table";
+import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon";
+import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
+import {ProjectProperty} from "karavan-core/lib/model/ProjectModel";
+
+interface Props {
+    properties: ProjectProperty[]
+    onChange?: (properties: ProjectProperty[]) => void
+}
+
+interface State {
+    properties: ProjectProperty[]
+    showDeleteConfirmation: boolean
+    deleteId?: string
+}
+
+export class PropertiesTable extends React.Component<Props, State> {
+
+    public state: State = {
+        properties: this.props.properties,
+        showDeleteConfirmation: false,
+    };
+
+    sendUpdate = (props: ProjectProperty[]) => {
+        this.props.onChange?.call(this, props);
+    }
+
+    changeProperty(p: ProjectProperty, field: "key" | "value", val?: string) {
+        const key: string = field === 'key' && val !== undefined ? val : p.key;
+        const value: any = field === 'value' ? val : p.value;
+        const property: ProjectProperty = {id: p.id, key: key, value: value};
+        const props = this.state.properties.map(prop => prop.id === property.id ? property : prop);
+        this.setState({properties: props});
+        this.sendUpdate(props);
+    }
+
+    startDelete(id: string) {
+        this.setState({showDeleteConfirmation: true, deleteId: id});
+    }
+
+    confirmDelete() {
+        const props = this.state.properties.filter(p => p.id !== this.state.deleteId);
+        this.setState({properties: props, showDeleteConfirmation: false, deleteId: undefined});
+        this.sendUpdate(props);
+    }
+
+    addProperty() {
+        const props = [...this.state.properties];
+        props.push(ProjectProperty.createNew("", ""))
+        this.setState({properties: props, showDeleteConfirmation: false, deleteId: undefined});
+        this.sendUpdate(props);
+    }
+
+    getDeleteConfirmation() {
+        return (<Modal
+            className="modal-delete"
+            title="Confirmation"
+            isOpen={this.state.showDeleteConfirmation}
+            onClose={() => this.setState({showDeleteConfirmation: false})}
+            actions={[
+                <Button key="confirm" variant="primary" onClick={e => this.confirmDelete()}>Delete</Button>,
+                <Button key="cancel" variant="link"
+                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+            ]}
+            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            <div>Delete property?</div>
+        </Modal>)
+    }
+
+    getTextInputField(property: ProjectProperty, field: "key" | "value", readOnly: boolean) {
+        return (<TextInput isDisabled={readOnly} isRequired={true} className="text-field" type={"text"} id={"key"} name={"key"}
+                           value={field === "key" ? property.key : property.value}
+                           onChange={val => this.changeProperty(property, field, val)}/>)
+    }
+
+    render() {
+        const properties = this.state.properties;
+        return (
+            <PageSection padding={{default: "noPadding"}}>
+                {properties.length > 0 &&
+                    <TableComposable aria-label="Property table" variant='compact' borders={false}
+                                     className="project-properties">
+                        <Thead>
+                            <Tr>
+                                <Th key='name'>Name</Th>
+                                <Th key='value'>Value</Th>
+                                <Td></Td>
+                            </Tr>
+                        </Thead>
+                        <Tbody>
+                            {properties.map((property, idx: number) => {
+                                const readOnly = property.key.startsWith("camel.jbang");
+                                return (
+                                    <Tr key={property.id}>
+                                        <Td noPadding width={20} dataLabel="key">{this.getTextInputField(property, "key", readOnly)}</Td>
+                                        <Td noPadding width={10} dataLabel="value">{this.getTextInputField(property, "value", readOnly)}</Td>
+                                        <Td noPadding isActionCell dataLabel="delete">
+                                            {!readOnly && <Button variant={"plain"} icon={<DeleteIcon/>} className={"delete-button"}
+                                                                  onClick={event => this.startDelete(property.id)}/>}
+                                        </Td>
+                                    </Tr>
+                                )})}
+                        </Tbody>
+                    </TableComposable>}
+                <Panel>
+                    <PanelMain>
+                        <PanelMainBody>
+                            <Flex direction={{default:"row"}} >
+                                <FlexItem align={{ default: 'alignRight' }}>
+                                    <Button isInline variant={"primary"} icon={<PlusIcon/>}
+                                            className={"add-button"}
+                                            onClick={event => this.addProperty()}>Add property</Button>
+                                </FlexItem>
+                            </Flex>
+                        </PanelMainBody>
+                    </PanelMain>
+                </Panel>
+            </PageSection>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-builder/openshift/karavan-quarkus-task.yaml b/karavan-builder/openshift/karavan-quarkus-task.yaml
index d9e9a79..d309c67 100644
--- a/karavan-builder/openshift/karavan-quarkus-task.yaml
+++ b/karavan-builder/openshift/karavan-quarkus-task.yaml
@@ -12,14 +12,22 @@ spec:
           #!/usr/bin/env bash
           CHECKOUT_DIR="/scripts"
 
-          git clone --depth 1 --branch main ${GIT_REPOSITORY} ${CHECKOUT_DIR}
-          
+          if  [[ $GIT_REPOSITORY == https* ]] ;
+          then
+              replacer=https://$GIT_PASSWORD@
+              prefix=https://
+              url="${GIT_REPOSITORY/$prefix/$replacer}"    
+              git clone --depth 1 --branch main $url ${CHECKOUT_DIR}
+          fi
+
           cd ${CHECKOUT_DIR}/$(inputs.params.project) 
 
           entrypoint -Dcamel.jbang.version=3.18.0-SNAPSHOT camel@apache/camel export
       
           export LAST_COMMIT=$(git rev-parse --short HEAD)
           export TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
+          export NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
+
           /opt/mvnd/bin/mvnd package \
             -Dquarkus.container-image.build=true \
             -Dquarkus.container-image.push=true \
@@ -30,7 +38,8 @@ spec:
             -Dquarkus.container-image.builder=jib \
             -Dquarkus.kubernetes-client.master-url=kubernetes.default.svc \
             -Dquarkus.kubernetes-client.token=${TOKEN} \
-            -Dquarkus.kubernetes.deploy=true
+            -Dquarkus.kubernetes.deploy=true \
+            -Dquarkus.container-image.group=${NAMESPACE}
       image: 'ghcr.io/apache/camel-karavan-builder:0.0.16'
       volumeMounts:
         - mountPath: /root/.m2