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 712ab0a6d1727bd44b1559f6ad517570262f893f Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Wed May 10 20:03:21 2023 -0400 Fixing issues for prototype #757 --- karavan-app/pom.xml | 4 ++ .../apache/camel/karavan/api/RunnerResource.java | 15 ++++-- .../camel/karavan/handler/PodEventHandler.java | 2 +- .../camel/karavan/service/InfinispanService.java | 23 ++++----- .../camel/karavan/service/KubernetesService.java | 55 +++++++++++++++++----- .../camel/karavan/service/RunnerStatusService.java | 4 +- .../src/main/resources/application.properties | 2 + karavan-app/src/main/webui/src/api/KaravanApi.tsx | 9 ++++ .../src/main/webui/src/projects/RunnerToolbar.tsx | 6 +-- .../camel/karavan/cli/resources/KaravanRole.java | 6 +-- karavan-runner/Dockerfile | 8 ++-- 11 files changed, 91 insertions(+), 43 deletions(-) diff --git a/karavan-app/pom.xml b/karavan-app/pom.xml index 255efbbd..654d668e 100644 --- a/karavan-app/pom.xml +++ b/karavan-app/pom.xml @@ -95,6 +95,10 @@ <groupId>io.quarkus</groupId> <artifactId>quarkus-openshift-client</artifactId> </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-container-image-docker</artifactId> + </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-minikube</artifactId> diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java b/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java index 998e8246..90027649 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/api/RunnerResource.java @@ -26,11 +26,7 @@ import javax.inject.Inject; import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; -import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; - -import static org.apache.camel.karavan.service.KubernetesService.RUNNER_SUFFIX; @Path("/api/runner") public class RunnerResource { @@ -49,7 +45,16 @@ public class RunnerResource { @Consumes(MediaType.APPLICATION_JSON) public String runProject(Project project) { Project p = infinispanService.getProject(project.getProjectId()); - return kubernetesService.tryCreatePod(p); + return kubernetesService.tryCreateRunner(p); + } + + @DELETE + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/{name}") + public Response deletePod(@PathParam("name") String name) throws Exception { + kubernetesService.deleteRunner(name); + return Response.accepted().build(); } @GET diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java b/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java index 9de06d30..8853655f 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/handler/PodEventHandler.java @@ -64,7 +64,7 @@ public class PodEventHandler implements ResourceEventHandler<Pod> { public PodStatus getPodStatus(Pod pod) { String deployment = pod.getMetadata().getLabels().get("app"); - String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("project"); + String project = deployment != null ? deployment : pod.getMetadata().getLabels().get("karavan/projectId"); try { boolean initialized = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Initialized")); boolean ready = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Ready")); diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java index b87efbd2..4c3dda1c 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/InfinispanService.java @@ -16,8 +16,6 @@ */ package org.apache.camel.karavan.service; -import io.quarkus.runtime.LaunchMode; -import io.quarkus.runtime.configuration.ProfileManager; import io.smallrye.mutiny.tuples.Tuple2; import org.apache.camel.karavan.model.CamelStatus; import org.apache.camel.karavan.model.DeploymentStatus; @@ -39,6 +37,7 @@ import org.infinispan.commons.api.CacheContainerAdmin; import org.infinispan.commons.configuration.StringConfiguration; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.cache.SingleFileStoreConfigurationBuilder; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.query.dsl.QueryFactory; @@ -91,18 +90,15 @@ public class InfinispanService implements HealthCheck { if (cacheManager == null) { LOGGER.info("InfinispanService is starting in local mode"); GlobalConfigurationBuilder global = GlobalConfigurationBuilder.defaultClusteredBuilder(); - // TODO: Analyse if we need persistence for local cache. -// global.globalState().enable().persistentLocation("karavan-data"); + global.globalState().enable().persistentLocation("/deployments/karavan-data"); DefaultCacheManager cacheManager = new DefaultCacheManager(global.build()); ConfigurationBuilder builder = new ConfigurationBuilder(); builder.clustering() .cacheMode(CacheMode.LOCAL) - // TODO: Analyse if we need persistence for local cache. -// .persistence().passivation(false) -// .addStore(SingleFileStoreConfigurationBuilder.class) -// .shared(false) -// .preload(true) - ; + .persistence().passivation(false) + .addStore(SingleFileStoreConfigurationBuilder.class) + .shared(false) + .preload(true); environments = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Environment.CACHE, builder.build()); projects = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(Project.CACHE, builder.build()); files = cacheManager.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE).getOrCreateCache(ProjectFile.CACHE, builder.build()); @@ -125,6 +121,7 @@ public class InfinispanService implements HealthCheck { camelStatuses = cacheManager.administration().getOrCreateCache(CamelStatus.CACHE, new StringConfiguration(String.format(CACHE_CONFIG, CamelStatus.CACHE))); commits = cacheManager.administration().getOrCreateCache("commits", new StringConfiguration(String.format(CACHE_CONFIG, "commits"))); } + System.out.println("READY"); ready.set(true); } @@ -348,11 +345,11 @@ public class InfinispanService implements HealthCheck { @Override public HealthCheckResponse call() { - if(ProfileManager.getLaunchMode() != LaunchMode.NORMAL && ready.get()){ + if (cacheManager == null && ready.get()){ return HealthCheckResponse.up("Infinispan Service is running in local mode."); } - else{ - if(this.getRemoteCacheManager() != null && this.getRemoteCacheManager().isStarted() && ready.get()) { + else { + if (cacheManager != null && cacheManager.isStarted() && ready.get()) { return HealthCheckResponse.up("Infinispan Service is running in cluster mode."); } else { 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 9874332b..006e5563 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 @@ -139,7 +139,7 @@ public class KubernetesService implements HealthCheck{ @ConsumeEvent(value = STOP_INFORMERS, blocking = true) void stopInformers(String data) { LOGGER.info("Stop Kubernetes Informers"); - informers.forEach(informer -> informer.close()); + informers.forEach(SharedIndexInformer::close); informers.clear(); } @@ -382,23 +382,33 @@ public class KubernetesService implements HealthCheck{ return result; } - public String tryCreatePod(Project project) { + public String tryCreateRunner(Project project) { String name = project.getProjectId() + "-" + RUNNER_SUFFIX; - createPVC(name + "-" + JBANG_CACHE_SUFFIX); - createPVC(name + "-" + M2_CACHE_SUFFIX); + createPVC(name + "-" + JBANG_CACHE_SUFFIX, name); + createPVC(name + "-" + M2_CACHE_SUFFIX, name); Pod old = kubernetesClient().pods().inNamespace(getNamespace()).withName(name).get(); if (old == null) { ProjectFile properties = infinispanService.getProjectFile(project.getProjectId(), APPLICATION_PROPERTIES_FILENAME); Map<String,String> containerResources = ServiceUtil .getRunnerContainerResourcesMap(properties, isOpenshift(), project.getRuntime().equals("quarkus")); - System.out.println(containerResources); Pod pod = getPod(project.getProjectId(), name, containerResources); Pod result = kubernetesClient().resource(pod).create(); LOGGER.info("Created pod " + result.getMetadata().getName()); } + createService(name); return name; } + public void deleteRunner(String name) { + try { + LOGGER.info("Delete runner: " + name + " in the namespace: " + getNamespace()); + kubernetesClient().pods().inNamespace(getNamespace()).withName(name).delete(); + kubernetesClient().services().inNamespace(getNamespace()).withName(name).delete(); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + } + } + public ResourceRequirements getResourceRequirements(Map<String,String> containerResources) { return new ResourceRequirementsBuilder() .addToRequests("cpu", new Quantity(containerResources.get("requests.cpu"))) @@ -411,8 +421,8 @@ public class KubernetesService implements HealthCheck{ private Pod getPod(String projectId, String name, Map<String,String> containerResources) { Map<String,String> labels = new HashMap<>(); labels.putAll(getRuntimeLabels()); - labels.putAll(getKaravanTypeLabel()); - labels.put("project", projectId); + labels.putAll(getKaravanRunnerLabels(name)); + labels.put("karavan/projectId", projectId); ResourceRequirements resources = getResourceRequirements(containerResources); @@ -453,14 +463,14 @@ public class KubernetesService implements HealthCheck{ .build(); } - private void createPVC(String pvcName) { + private void createPVC(String pvcName, String runnerName) { PersistentVolumeClaim old = kubernetesClient().persistentVolumeClaims().inNamespace(getNamespace()).withName(pvcName).get(); if (old == null) { PersistentVolumeClaim pvc = new PersistentVolumeClaimBuilder() .withNewMetadata() .withName(pvcName) .withNamespace(getNamespace()) - .withLabels(getKaravanTypeLabel()) + .withLabels(getKaravanRunnerLabels(runnerName)) .endMetadata() .withNewSpec() .withResources(new ResourceRequirementsBuilder().withRequests(Map.of("storage", new Quantity("2Gi"))).build()) @@ -468,10 +478,30 @@ public class KubernetesService implements HealthCheck{ .withAccessModes("ReadWriteOnce") .endSpec() .build(); - kubernetesClient().resource(pvc).create(); + kubernetesClient().resource(pvc).createOrReplace(); } } + private void createService(String name) { + + ServicePortBuilder portBuilder = new ServicePortBuilder() + .withName("http").withPort(80).withProtocol("TCP").withTargetPort(new IntOrString(8080)); + + Service service = new ServiceBuilder() + .withNewMetadata() + .withName(name) + .withNamespace(getNamespace()) + .withLabels(getKaravanRunnerLabels(name)) + .endMetadata() + .withNewSpec() + .withType("ClusterIP") + .withPorts(portBuilder.build()) + .withSelector(getKaravanRunnerLabels(name)) + .endSpec() + .build(); + kubernetesClient().resource(service).createOrReplace(); + } + public Secret getKaravanSecret() { return kubernetesClient().secrets().inNamespace(getNamespace()).withName("karavan").get(); } @@ -492,8 +522,9 @@ public class KubernetesService implements HealthCheck{ return map; } - public static Map<String, String> getKaravanTypeLabel() { - return Map.of("karavan/type" , "runner"); + public static Map<String, String> getKaravanRunnerLabels(String name) { + return Map.of("karavan/type" , "runner", + "app.kubernetes.io/name", name); } public boolean isOpenshift() { diff --git a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java index 4ae496c9..e8c7bb1f 100644 --- a/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java +++ b/karavan-app/src/main/java/org/apache/camel/karavan/service/RunnerStatusService.java @@ -76,8 +76,8 @@ public class RunnerStatusService { @ConsumeEvent(value = CMD_COLLECT_RUNNER_STATUS, blocking = true, ordered = false) public void collectRunnerStatuses(String podName) throws ExecutionException, InterruptedException { String url = "http://" + podName + "." + kubernetesService.getNamespace() + ".svc.cluster.local/q/dev"; -// HttpResponse<Buffer> result = bufferResult(url, 100); -// System.out.println(result); + HttpResponse<Buffer> result = bufferResult(url, 100); + System.out.println(result); } @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000) diff --git a/karavan-app/src/main/resources/application.properties b/karavan-app/src/main/resources/application.properties index bfd804ca..37449a07 100644 --- a/karavan-app/src/main/resources/application.properties +++ b/karavan-app/src/main/resources/application.properties @@ -18,6 +18,7 @@ karavan.git-pull-interval=disabled quarkus.infinispan-client.devservices.enabled=false quarkus.infinispan-client.devservices.port=12345 quarkus.infinispan-client.devservices.service-name=karavan +quarkus.infinispan-client.health.enabled=false # Authentication quarkus.infinispan-client.username=admin quarkus.infinispan-client.password=password @@ -87,6 +88,7 @@ quarkus.qute.strict-rendering=false quarkus.qute.property-not-found-strategy=output-original quarkus.container-image.builder=docker +quarkus.health.extensions.enabled=false quarkus.kubernetes-client.connection-timeout=2000 quarkus.kubernetes-client.request-timeout=10000 diff --git a/karavan-app/src/main/webui/src/api/KaravanApi.tsx b/karavan-app/src/main/webui/src/api/KaravanApi.tsx index 78736f22..99ea5dfa 100644 --- a/karavan-app/src/main/webui/src/api/KaravanApi.tsx +++ b/karavan-app/src/main/webui/src/api/KaravanApi.tsx @@ -318,6 +318,15 @@ export class KaravanApi { }); } + static async deleteRunner(name: string, after: (res: AxiosResponse<any>) => void) { + instance.delete('/api/runner/' + name) + .then(res => { + after(res); + }).catch(err => { + after(err); + }); + } + static async pipelineRun(project: Project, environment: string, after: (res: AxiosResponse<any>) => void) { instance.post('/api/kubernetes/pipeline/' + environment, project) .then(res => { diff --git a/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx b/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx index b2944ed7..532b91bb 100644 --- a/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx +++ b/karavan-app/src/main/webui/src/projects/RunnerToolbar.tsx @@ -39,9 +39,9 @@ export const RunnerToolbar = (props: Props) => { }); } - function deletePod () { + function deleteRunner () { setIsDeletingPod(true); - KaravanApi.deletePod(props.config.environment, podName, res => { + KaravanApi.deleteRunner(podName, res => { if (res.status === 202) { setIsDeletingPod(false); } else { @@ -87,7 +87,7 @@ export const RunnerToolbar = (props: Props) => { variant={"secondary"} className="project-button" icon={!isRunning ? <DeleteIcon/> : <div></div>} - onClick={() => deletePod()}> + onClick={() => deleteRunner()}> {isDeletingPod ? "..." : "Delete"} </Button> </Tooltip> diff --git a/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java b/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java index f2e14ba7..e93b72eb 100644 --- a/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java +++ b/karavan-cli/src/main/java/org/apache/camel/karavan/cli/resources/KaravanRole.java @@ -29,11 +29,11 @@ public class KaravanRole { .withNamespace(config.getNamespace()) .endMetadata() .withRules( - new PolicyRuleBuilder().withApiGroups("").withResources("secrets", "configmaps").withVerbs("get", "list").build(), - new PolicyRuleBuilder().withApiGroups("").withResources("persistentvolumes", "persistentvolumeclaims").withVerbs("get", "list", "watch").build(), + new PolicyRuleBuilder().withApiGroups("").withResources("secrets", "configmaps").withVerbs("*").build(), + new PolicyRuleBuilder().withApiGroups("").withResources("persistentvolumes", "persistentvolumeclaims").withVerbs("*").build(), new PolicyRuleBuilder().withApiGroups("tekton.dev").withResources("pipelineruns").withVerbs("*").build(), new PolicyRuleBuilder().withApiGroups("").withResources("pods", "services", "replicationcontrollers").withVerbs("*").build(), - new PolicyRuleBuilder().withApiGroups("route.openshift.io").withResources( "routes").withVerbs("*").build(), + new PolicyRuleBuilder().withApiGroups("route.openshift.io").withResources("routes").withVerbs("*").build(), new PolicyRuleBuilder().withApiGroups("apps").withResources("deployments").withVerbs("*").build() ) .build(); diff --git a/karavan-runner/Dockerfile b/karavan-runner/Dockerfile index 9d3febf7..8bc9f357 100644 --- a/karavan-runner/Dockerfile +++ b/karavan-runner/Dockerfile @@ -1,15 +1,15 @@ FROM jbangdev/jbang-action:0.106.1 -ENV CAMEL_VERSION=3.20.4 +ENV CAMEL_VERSION=3.21.0-SNAPSHOT ENV KAMELETS_DIR="/kamelets" RUN mkdir /kamelets # Install Camel-JBang RUN jbang trust add -o --fresh --quiet https://github.com/apache/camel/blob/camel-$CAMEL_VERSION/dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java -RUN jbang alias add --name camel https://github.com/apache/camel/blob/camel-$CAMEL_VERSION/dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java +RUN jbang trust add -o --fresh --quiet https://github.com/apache/camel/blob/HEAD/dsl/camel-jbang/camel-jbang-main/dist/CamelJBang.java # Add demo routes -COPY demo.camel.yaml /scripts/demo.camel.yaml +# COPY demo.camel.yaml /scripts/demo.camel.yaml WORKDIR /scripts -ENTRYPOINT jbang -Dcamel.jbang.version=$CAMEL_VERSION camel run * --console --local-kamelet-dir=$KAMELETS_DIR \ No newline at end of file +ENTRYPOINT jbang -Dcamel.jbang.version=$CAMEL_VERSION camel@apache/camel run --source-dir=/scripts --console --local-kamelet-dir=$KAMELETS_DIR \ No newline at end of file