This is an automated email from the ASF dual-hosted git repository.

claudio4j pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new a6107a2132d CAMEL-21835 CAMEL-21841 knative service not working in 
camel-jbang-plugin-kubernetes (#17508)
a6107a2132d is described below

commit a6107a2132dcae2fd2259051971284decfc0c4ff
Author: Claudio Miranda <clau...@claudius.com.br>
AuthorDate: Fri Mar 21 07:19:19 2025 -0300

    CAMEL-21835 CAMEL-21841 knative service not working in 
camel-jbang-plugin-kubernetes (#17508)
---
 .../modules/ROOT/pages/camel-jbang-kubernetes.adoc |   5 +
 .../core/commands/kubernetes/ClusterType.java      |   3 +-
 .../core/commands/kubernetes/KubernetesDelete.java |  33 ++++-
 .../core/commands/kubernetes/KubernetesHelper.java |  21 +++
 .../core/commands/kubernetes/KubernetesRun.java    |  56 +++++++-
 .../commands/kubernetes/traits/ContainerTrait.java |  18 ++-
 .../kubernetes/traits/DeploymentTrait.java         |   7 +-
 .../traits/knative/KnativeServiceTrait.java        |  17 +++
 .../commands/kubernetes/KubernetesDeleteTest.java  |  12 ++
 .../kubernetes/KubernetesExportKnativeTest.java    |   2 +-
 .../kubernetes/KubernetesRunCustomTest.java        | 142 +++++++++++++++------
 11 files changed, 260 insertions(+), 56 deletions(-)

diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
index 2d4494dafe0..cac96c85cec 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-kubernetes.adoc
@@ -481,6 +481,11 @@ The Knative service trait will create such a resource as 
part of the Kubernetes
 
 NOTE: The `knative-service` trait is disabled by default, you need to enable 
the Knative service trait with `--trait knative-service.enabled=true` option. 
Otherwise, the Camel JBang export will always create an arbitrary Kubernetes 
service resource.
 
+NOTE: If you enable the knative-service trait and deploys to minikube and uses 
the docker builder to build the container image in the minikube registry addon, 
it's likely the container image published won't contain the digest value, and 
https://knative.dev/docs/serving/configuration/deployment/#skipping-tag-resolution[knative-serving
 has a verification to check the image digest], so the pod will fail to start 
with the error message `failed to resolve image to digest`, so for development  
[...]
+
+WARN: Currently knative-service doesn't work in OpenShift, work is underway to 
fix it.
+
+
 The trait offers following options for customization:
 
 [cols="2m,1m,5a"]
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/ClusterType.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/ClusterType.java
index c30ece52e34..d63e599701b 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/ClusterType.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/ClusterType.java
@@ -39,7 +39,6 @@ public enum ClusterType {
         if (clusterType == null) {
             return false;
         }
-
-        return this == fromName(clusterType);
+        return this.name().equalsIgnoreCase(clusterType);
     }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
index f60790bc02f..c22c3823f88 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDelete.java
@@ -23,9 +23,11 @@ import java.util.List;
 import java.util.Map;
 
 import io.fabric8.kubernetes.api.model.StatusDetails;
+import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext;
 import io.fabric8.openshift.client.OpenShiftClient;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
+import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.StringHelper;
 import org.codehaus.plexus.util.ExceptionUtils;
 import picocli.CommandLine;
@@ -46,6 +48,9 @@ public class KubernetesDelete extends KubernetesBaseCommand {
 
     public Integer doCall() throws Exception {
         printer().printf("Deleting all resources from app: %s%n", appName);
+        if (ObjectHelper.isEmpty(namespace)) {
+            namespace = getKubernetesClient().getNamespace();
+        }
         Map<String, String> labels = new HashMap<>();
         // this label is set in KubernetesRun command
         labels.put(BaseTrait.KUBERNETES_LABEL_MANAGED_BY, "camel-jbang");
@@ -54,23 +59,39 @@ public class KubernetesDelete extends KubernetesBaseCommand 
{
         try {
             // delete the most common resources
             // delete Deployment cascades to pod
-            
deleteStatuses.addAll(getKubernetesClient().apps().deployments().withLabels(labels).delete());
+            deleteStatuses
+                    
.addAll(getKubernetesClient().apps().deployments().inNamespace(namespace).withLabels(labels).delete());
             // delete service
-            
deleteStatuses.addAll(getKubernetesClient().services().withLabels(labels).delete());
+            
deleteStatuses.addAll(getKubernetesClient().services().inNamespace(namespace).withLabels(labels).delete());
+            // delete knative-services
+            ResourceDefinitionContext knativeServices = new 
ResourceDefinitionContext.Builder()
+                    .withGroup("serving.knative.dev")
+                    .withVersion("v1")
+                    .withKind("Service")
+                    .withNamespaced(true)
+                    .build();
+            deleteStatuses
+                    
.addAll(getKubernetesClient().genericKubernetesResources(knativeServices).inNamespace(namespace)
+                            .withLabels(labels).delete());
+            // delete configmap
+            
deleteStatuses.addAll(getKubernetesClient().configMaps().inNamespace(namespace).withLabels(labels).delete());
+            // delete secrets
+            
deleteStatuses.addAll(getKubernetesClient().secrets().inNamespace(namespace).withLabels(labels).delete());
             ClusterType clusterType = KubernetesHelper.discoverClusterType();
             if (ClusterType.OPENSHIFT == clusterType) {
                 // openshift specific: BuildConfig, ImageStreams, Route - 
BuildConfig casacade delete to Build and ConfigMap
                 OpenShiftClient ocpClient = 
getKubernetesClient().adapt(OpenShiftClient.class);
                 // BuildConfig
-                
deleteStatuses.addAll(ocpClient.buildConfigs().withLabels(labels).delete());
+                
deleteStatuses.addAll(ocpClient.buildConfigs().inNamespace(namespace).withLabels(labels).delete());
                 // ImageStreams
-                
deleteStatuses.addAll(ocpClient.imageStreams().withLabels(labels).delete());
+                
deleteStatuses.addAll(ocpClient.imageStreams().inNamespace(namespace).withLabels(labels).delete());
                 // Route
-                
deleteStatuses.addAll(ocpClient.routes().withLabels(labels).delete());
+                
deleteStatuses.addAll(ocpClient.routes().inNamespace(namespace).withLabels(labels).delete());
             }
             if (deleteStatuses.size() > 0) {
                 deleteStatuses.forEach(
-                        s -> printer().printf("Deleted: %s '%s'%n", 
StringHelper.capitalize(s.getKind()), s.getName()));
+                        s -> printer().printf("Deleted: %s/%s '%s'%n", 
s.getGroup(), StringHelper.capitalize(s.getKind()),
+                                s.getName()));
             } else {
                 printer().println("No deployment found with name: " + appName);
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
index 71fbf2349a5..507d8cb0911 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesHelper.java
@@ -32,6 +32,8 @@ import com.fasterxml.jackson.databind.MapperFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.json.JsonMapper;
+import io.fabric8.kubernetes.api.model.ConfigMap;
+import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
 import io.fabric8.kubernetes.api.model.GenericKubernetesResource;
 import io.fabric8.kubernetes.api.model.GenericKubernetesResourceList;
 import io.fabric8.kubernetes.api.model.Pod;
@@ -188,6 +190,25 @@ public final class KubernetesHelper {
         return minikube;
     }
 
+    // when minikube is used with the registry addon exposed
+    // the build of images uses docker builder directly from the registry 
inside minikube
+    // that doesn't generate the container image digest in the registry
+    // that poses a problem when deploying a knative-service, since it 
validates the container image to have a digest
+    // then it fails with: failed to resolve image to digest
+    // so, for development purposes we disable this validation in minikube
+    // 
https://knative.dev/docs/serving/configuration/deployment/#skipping-tag-resolution
+    public static void skipKnativeImageTagResolutionInMinikube() {
+        ConfigMap cm = 
getKubernetesClient().configMaps().inNamespace("knative-serving").withName("config-deployment").get();
+        Map<String, String> data = cm.getData();
+        String skipTag = data.get("registries-skipping-tag-resolving");
+        if (skipTag == null || !skipTag.contains("localhost:5000")) {
+            // patch the cm/config-deployment in knative-serving namespace with
+            // registries-skipping-tag-resolving: localhost:5000
+            
getKubernetesClient().configMaps().inNamespace("knative-serving").withName("config-deployment").edit(
+                    c -> new 
ConfigMapBuilder(c).addToData("registries-skipping-tag-resolving", 
"localhost:5000").build());
+        }
+    }
+
     /**
      * Sanitize given name to meet Kubernetes resource naming requirements.
      *
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
index 0fa05b85375..45e17cbb6ed 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRun.java
@@ -37,6 +37,8 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.dsl.jbang.core.commands.CommandHelper;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitHelper;
+import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Traits;
 import org.apache.camel.dsl.jbang.core.common.Printer;
 import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates;
 import org.apache.camel.dsl.jbang.core.common.RuntimeType;
@@ -257,7 +259,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
                         description = "Maven/Gradle build properties, ex. 
--build-property=prop1=foo")
     List<String> buildProperties = new ArrayList<>();
 
-    @CommandLine.Option(names = { "--disable-auto" }, description = "Disable 
automatic cluster type detection.")
+    @CommandLine.Option(names = { "--disable-auto" },
+                        description = "Disable automatic cluster type 
detection and automatic settings for cluster.")
     boolean disableAuto = false;
 
     // DevMode/Reload state
@@ -297,7 +300,10 @@ public class KubernetesRun extends KubernetesBaseCommand {
         }
 
         if (output != null) {
-            exit = buildProject(workingDir);
+            Traits ptraits = TraitHelper.parseTraits(traits);
+            boolean ksvcEnabled = ptraits.getKnativeService() != null && 
ptraits.getKnativeService().getEnabled();
+
+            exit = buildProjectOutput(workingDir);
             if (exit != 0) {
                 printer().printErr("Project build failed!");
                 return exit;
@@ -305,8 +311,14 @@ public class KubernetesRun extends KubernetesBaseCommand {
 
             File manifest;
             switch (output) {
-                case "yaml" ->
-                    manifest = 
KubernetesHelper.resolveKubernetesManifest(clusterType, workingDir + 
"/target/kubernetes");
+                case "yaml" -> {
+                    if (ksvcEnabled) {
+                        // trick the clusterType to be able to read from the 
jkube source directory
+                        manifest = 
KubernetesHelper.resolveKubernetesManifest("service", workingDir + 
"/src/main/jkube");
+                    } else {
+                        manifest = 
KubernetesHelper.resolveKubernetesManifest(clusterType, workingDir + 
"/target/kubernetes");
+                    }
+                }
                 case "json" ->
                     manifest = 
KubernetesHelper.resolveKubernetesManifest(clusterType, workingDir + 
"/target/kubernetes",
                             "json");
@@ -558,7 +570,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
         return client;
     }
 
-    private Integer buildProject(String workingDir) throws IOException, 
InterruptedException {
+    // build the project locally to generate the artifacts in the target 
directory, but don't deploy it.
+    private Integer buildProjectOutput(String workingDir) throws IOException, 
InterruptedException {
         printer().println("Building Camel application ...");
 
         // Run build via Maven
@@ -586,6 +599,15 @@ public class KubernetesRun extends KubernetesBaseCommand {
         // suppress maven transfer progress
         args.add("-ntp");
 
+        Traits ptraits = TraitHelper.parseTraits(traits);
+        if (ptraits.getKnativeService() != null && 
ptraits.getKnativeService().getEnabled()) {
+            // by default jkube creates a Deployment manifest and it doesn't 
support knative controller yet.
+            // however when knative-service is enabled the knative-service 
trait generates a src/main/jkube/service.yml
+            // and there is no need for the regular Deployment as the knative 
Service manifest, once deployed
+            // will generate the regular Deployment, so we have to disable the 
jkube resources task to not run and not generate the deployment.yml
+            args.add("-Djkube.skip.resource=true");
+        }
+
         args.add("package");
 
         printer().println("Run: " + String.join(" ", args));
@@ -626,6 +648,8 @@ public class KubernetesRun extends KubernetesBaseCommand {
         if (quiet || !verbose) {
             args.add("--quiet");
         }
+        // suppress maven transfer progress
+        args.add("-ntp");
         args.add("--file");
         args.add(workingDir);
 
@@ -644,7 +668,27 @@ public class KubernetesRun extends KubernetesBaseCommand {
 
         boolean isOpenshift = ClusterType.OPENSHIFT.isEqualTo(clusterType);
         var prefix = isOpenshift ? "oc" : "k8s";
-        args.add(prefix + ":deploy");
+        Traits ptraits = TraitHelper.parseTraits(traits);
+        if (ptraits.getKnativeService() != null && 
ptraits.getKnativeService().getEnabled()) {
+            // by default jkube creates a Deployment manifest and it doesn't 
support knative controller yet.
+            // however when knative-service is enabled the knative-service 
trait generates a src/main/jkube/service.yml
+            // and there is no need for the regular Deployment as the knative 
Service manifest, once deployed
+            // will generate the regular Deployment, so we have to disable the 
jkube resources task to not run and not generate the deployment.yml
+            // apply the knative service manifest and specify the knative 
service.yml
+            args.add("-Djkube.skip.resource=true");
+            args.add(prefix + ":build");
+            args.add(prefix + ":apply");
+            if (isOpenshift) {
+                
args.add("-Djkube.openshiftManifest=src/main/jkube/service.yml");
+            } else {
+                
args.add("-Djkube.kubernetesManifest=src/main/jkube/service.yml");
+            }
+            if (ClusterType.MINIKUBE.isEqualTo(clusterType) && !disableAuto) {
+                KubernetesHelper.skipKnativeImageTagResolutionInMinikube();
+            }
+        } else {
+            args.add(prefix + ":deploy");
+        }
 
         printer().println("Run: " + String.join(" ", args));
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
index ca3c292229b..549f7f58241 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/ContainerTrait.java
@@ -31,6 +31,7 @@ public class ContainerTrait extends BaseTrait {
     public static final int CONTAINER_TRAIT_ORDER = 1600;
     public static final int DEFAULT_CONTAINER_PORT = 8080;
     public static final String DEFAULT_CONTAINER_PORT_NAME = "http";
+    public static final String KNATIVE_CONTAINER_PORT_NAME = "h2c";
 
     public ContainerTrait() {
         super("container", CONTAINER_TRAIT_ORDER);
@@ -55,8 +56,11 @@ public class ContainerTrait extends BaseTrait {
         }
 
         if (containerTrait.getPort() != null || 
context.getService().isPresent() || context.getKnativeService().isPresent()) {
+            String portName = context.getKnativeService().isPresent()
+                    ? KNATIVE_CONTAINER_PORT_NAME
+                    : 
Optional.ofNullable(containerTrait.getPortName()).orElse(DEFAULT_CONTAINER_PORT_NAME);
             container.addToPorts(new ContainerPortBuilder()
-                    
.withName(Optional.ofNullable(containerTrait.getPortName()).orElse(DEFAULT_CONTAINER_PORT_NAME))
+                    .withName(portName)
                     .withContainerPort(
                             
Optional.ofNullable(containerTrait.getPort()).map(Long::intValue).orElse(DEFAULT_CONTAINER_PORT))
                     .withProtocol("TCP")
@@ -78,6 +82,18 @@ public class ContainerTrait extends BaseTrait {
         }
         container.withResources(resourceRequirementsBuilder.build());
 
+        io.fabric8.kubernetes.api.model.Container cc = container.build();
+        context.doWithKnativeServices(s -> s.editOrNewSpec()
+                .editOrNewTemplate()
+                .editOrNewMetadata()
+                .addToLabels(KUBERNETES_LABEL_NAME, context.getName())
+                .endMetadata()
+                .editOrNewSpec()
+                .addToContainers(cc)
+                .endSpec()
+                .endTemplate()
+                .endSpec());
+
         context.doWithDeployments(d -> d.editOrNewSpec()
                 .editOrNewTemplate()
                 .editOrNewMetadata()
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
index 32bc02578c7..35fa071e68d 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/DeploymentTrait.java
@@ -34,7 +34,12 @@ public class DeploymentTrait extends BaseTrait {
 
     @Override
     public boolean configure(Traits traitConfig, TraitContext context) {
-        return true;
+        // disable the deployment trait if knative-service is enabled
+        boolean knEnabled = false;
+        if (traitConfig.getKnativeService() != null) {
+            knEnabled = 
Optional.ofNullable(traitConfig.getKnativeService().getEnabled()).orElse(false);
+        }
+        return !knEnabled;
     }
 
     @Override
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeServiceTrait.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeServiceTrait.java
index 0507fe5fe21..d0a2a9c8a6d 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeServiceTrait.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/main/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/traits/knative/KnativeServiceTrait.java
@@ -18,6 +18,7 @@
 package org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.knative;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
@@ -26,6 +27,7 @@ import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.BaseTrait;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.ServiceTrait;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitContext;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.TraitHelper;
+import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Container;
 import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.KnativeService;
 import org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.model.Traits;
 import org.apache.camel.util.ObjectHelper;
@@ -94,6 +96,12 @@ public class KnativeServiceTrait extends KnativeBaseTrait {
 
         Map<String, String> serviceLabels = new HashMap<>();
         serviceLabels.put(BaseTrait.KUBERNETES_LABEL_NAME, context.getName());
+        // add the same labels added by jkube, since for knative-serving the 
jkube resource generation is disabled
+        serviceLabels.put(BaseTrait.KUBERNETES_LABEL_MANAGED_BY, 
"camel-jbang");
+        serviceLabels.put("app", context.getName());
+        serviceLabels.put("app.kubernetes.io/version", context.getVersion());
+        serviceLabels.put("provider", "jkube");
+        serviceLabels.put("version", context.getVersion());
 
         // Make sure the Eventing webhook will select the source resource, in 
order to inject the sink information.
         // This is necessary for Knative environments, that are configured 
with SINK_BINDING_SELECTION_MODE=inclusion.
@@ -120,6 +128,15 @@ public class KnativeServiceTrait extends KnativeBaseTrait {
                 .endTemplate()
                 .endSpec();
 
+        Container containerTrait = 
Optional.ofNullable(traitConfig.getContainer()).orElseGet(Container::new);
+        
Optional.ofNullable(containerTrait.getImagePullSecrets()).orElseGet(List::of).forEach(sec
 -> service.editSpec()
+                .editOrNewTemplate()
+                .editOrNewSpec()
+                .addNewImagePullSecret(sec)
+                .endSpec()
+                .endTemplate()
+                .endSpec());
+
         if (serviceTrait.getTimeoutSeconds() != null && 
serviceTrait.getTimeoutSeconds() > 0) {
             service.editSpec()
                     .editTemplate()
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteTest.java
index 159ee6307e9..bd92ecfcbb9 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesDeleteTest.java
@@ -134,6 +134,12 @@ class KubernetesDeleteTest {
                 
"/apis/apps/v1/namespaces/test/deployments?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
         serverDeleteExpect(
                 
"/api/v1/namespaces/test/services?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
+        serverDeleteExpect(
+                
"/apis/serving.knative.dev/v1/namespaces/test/services?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
+        serverDeleteExpect(
+                
"/api/v1/namespaces/test/configmaps?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
+        serverDeleteExpect(
+                
"/api/v1/namespaces/test/secrets?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
 
         // Execute delete command
         KubernetesDelete command = new KubernetesDelete(new 
CamelJBangMain().withPrinter(printer));
@@ -176,6 +182,12 @@ class KubernetesDeleteTest {
                 
"/apis/image.openshift.io/v1/namespaces/test/imagestreams?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
         serverDeleteExpect(
                 
"/apis/route.openshift.io/v1/namespaces/test/routes?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
+        serverDeleteExpect(
+                
"/apis/serving.knative.dev/v1/namespaces/test/services?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
+        serverDeleteExpect(
+                
"/api/v1/namespaces/test/configmaps?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
+        serverDeleteExpect(
+                
"/api/v1/namespaces/test/secrets?labelSelector=app%3Dmy-route%2Capp.kubernetes.io%2Fmanaged-by%3Dcamel-jbang");
 
         // Execute delete command
         KubernetesDelete command = new KubernetesDelete(new 
CamelJBangMain().withPrinter(printer));
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportKnativeTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportKnativeTest.java
index c3e345198df..39b798ac3e7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportKnativeTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesExportKnativeTest.java
@@ -75,7 +75,7 @@ public class KubernetesExportKnativeTest extends 
KubernetesExportBaseTest {
         var labelsB = 
service.getSpec().getTemplate().getMetadata().getLabels();
         Map<String, String> annotations = 
service.getSpec().getTemplate().getMetadata().getAnnotations();
         Assertions.assertEquals("route-service", 
service.getMetadata().getName());
-        Assertions.assertEquals(3, labelsA.size());
+        Assertions.assertEquals(8, labelsA.size());
         Assertions.assertEquals("route-service", 
labelsA.get(BaseTrait.KUBERNETES_LABEL_NAME));
         Assertions.assertEquals("true", 
labelsA.get("bindings.knative.dev/include"));
         Assertions.assertEquals("cluster-local", 
labelsA.get("networking.knative.dev/visibility"));
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunCustomTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunCustomTest.java
index ad524b3aa3c..d51d4f2b359 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunCustomTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-kubernetes/src/test/java/org/apache/camel/dsl/jbang/core/commands/kubernetes/KubernetesRunCustomTest.java
@@ -29,12 +29,16 @@ import io.fabric8.kubernetes.api.model.Node;
 import io.fabric8.kubernetes.api.model.NodeBuilder;
 import io.fabric8.kubernetes.api.model.NodeList;
 import io.fabric8.kubernetes.api.model.NodeListBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
 import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
 import io.fabric8.openshift.api.model.config.v1.ClusterVersion;
 import io.fabric8.openshift.api.model.config.v1.ClusterVersionBuilder;
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import 
org.apache.camel.dsl.jbang.core.commands.kubernetes.traits.ContainerTrait;
 import org.apache.camel.dsl.jbang.core.common.StringPrinter;
 import org.apache.camel.dsl.jbang.core.common.VersionHelper;
 import org.junit.jupiter.api.Assertions;
@@ -70,19 +74,7 @@ class KubernetesRunCustomTest {
     @Test
     public void disableAutomaticClusterDetection() throws Exception {
         KubernetesHelper.setKubernetesClient(client);
-        ClusterVersion versionCR = new ClusterVersionBuilder()
-                .withNewMetadata().withName("version").endMetadata()
-                .withNewStatus()
-                .withNewDesired()
-                .withVersion("4.14.5")
-                .endDesired()
-                .endStatus()
-                .build();
-
-        
server.expect().get().withPath("/apis/config.openshift.io/v1/clusterversions/version")
-                .andReturn(HttpURLConnection.HTTP_OK, versionCR)
-                .once();
-
+        setupServerExpectsOpenshift();
         KubernetesRun command = createCommand(new String[] { 
"classpath:route.yaml" },
                 "--image-registry=quay.io", "--image-group=camel-test", 
"--output=yaml",
                 "--disable-auto");
@@ -100,19 +92,7 @@ class KubernetesRunCustomTest {
     @Test
     public void detectOpenshiftCluster() throws Exception {
         KubernetesHelper.setKubernetesClient(client);
-        ClusterVersion versionCR = new ClusterVersionBuilder()
-                .withNewMetadata().withName("version").endMetadata()
-                .withNewStatus()
-                .withNewDesired()
-                .withVersion("4.14.5")
-                .endDesired()
-                .endStatus()
-                .build();
-
-        
server.expect().get().withPath("/apis/config.openshift.io/v1/clusterversions/version")
-                .andReturn(HttpURLConnection.HTTP_OK, versionCR)
-                .once();
-
+        setupServerExpectsOpenshift();
         KubernetesRun command = createCommand(new String[] { 
"classpath:route.yaml" },
                 "--image-registry=quay.io", "--image-group=camel-test", 
"--output=yaml");
         int exit = command.doCall();
@@ -131,19 +111,7 @@ class KubernetesRunCustomTest {
     @SetEnvironmentVariable(key = "DOCKER_TLS_VERIFY", value = "foo")
     public void detectMinikubeCluster() throws Exception {
         KubernetesHelper.setKubernetesClient(client);
-        Node nodeCR = new NodeBuilder()
-                .withNewMetadata()
-                .withName("minikube")
-                .withLabels(Collections.singletonMap("minikube.k8s.io/name", 
"minikube"))
-                .endMetadata()
-                .build();
-        NodeList nodeList = new NodeListBuilder().addToItems(nodeCR)
-                .build();
-
-        
server.expect().get().withPath("/api/v1/nodes?labelSelector=minikube.k8s.io%2Fname")
-                .andReturn(HttpURLConnection.HTTP_OK, nodeList)
-                .once();
-
+        setupServerExpectsMinikube();
         KubernetesRun command = createCommand(new String[] { 
"classpath:route.yaml" },
                 "--image-registry=quay.io", "--image-group=camel-test", 
"--output=yaml");
         int exit = command.doCall();
@@ -160,6 +128,102 @@ class KubernetesRunCustomTest {
         Assertions.assertEquals("docker", command.imageBuilder);
     }
 
+    @Test
+    @SetEnvironmentVariable(key = "MINIKUBE_ACTIVE_DOCKERD", value = "foo")
+    @SetEnvironmentVariable(key = "DOCKER_TLS_VERIFY", value = "foo")
+    public void shouldGenerateKnativeService() throws Exception {
+        KubernetesHelper.setKubernetesClient(client);
+        setupServerExpectsMinikube();
+        KubernetesRun command = createCommand(new String[] { 
"classpath:route-service.yaml" },
+                "--trait", "knative-service.enabled=true",
+                "--image-registry=quay.io", "--image-group=camel-test", 
"--output=yaml");
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        Assertions.assertEquals(ClusterType.MINIKUBE.name().toLowerCase(), 
command.clusterType.toLowerCase());
+
+        // as the k8s:resource task is skipped for knative-service, there 
won't be a kubernetes.yml
+        // so, we add a triple dash to emulate the first line of the 
kubernetes.yml
+        String output = "---" + System.lineSeparator() + printer.getOutput();
+        var manifest = 
KubernetesBaseTest.getKubernetesManifestAsStream(output, command.output);
+        List<HasMetadata> resources = client.load(manifest).items();
+        // expects KnativeService only
+        Assertions.assertEquals(1, resources.size());
+
+        io.fabric8.knative.serving.v1.Service ksvc = resources.stream()
+                .filter(it -> 
io.fabric8.knative.serving.v1.Service.class.isAssignableFrom(it.getClass()))
+                .map(io.fabric8.knative.serving.v1.Service.class::cast)
+                .findFirst()
+                .orElseThrow(() -> new RuntimeCamelException("Missing 
KnativeService in Kubernetes manifest"));
+
+        var containers = 
ksvc.getSpec().getTemplate().getSpec().getContainers();
+        Assertions.assertEquals(ContainerTrait.KNATIVE_CONTAINER_PORT_NAME, 
containers.get(0).getPorts().get(0).getName());
+    }
+
+    @Test
+    @SetEnvironmentVariable(key = "MINIKUBE_ACTIVE_DOCKERD", value = "foo")
+    @SetEnvironmentVariable(key = "DOCKER_TLS_VERIFY", value = "foo")
+    public void shouldGenerateRegularService() throws Exception {
+        KubernetesHelper.setKubernetesClient(client);
+        setupServerExpectsMinikube();
+        KubernetesRun command = createCommand(new String[] { 
"classpath:route-service.yaml" },
+                "--image-registry=quay.io", "--image-group=camel-test", 
"--output=yaml");
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        Assertions.assertEquals(ClusterType.MINIKUBE.name().toLowerCase(), 
command.clusterType.toLowerCase());
+
+        var manifest = 
KubernetesBaseTest.getKubernetesManifestAsStream(printer.getOutput(), 
command.output);
+        List<HasMetadata> resources = client.load(manifest).items();
+        // expects service and deployment only
+        Assertions.assertEquals(2, resources.size());
+
+        Service svc = resources.stream()
+                .filter(it -> Service.class.isAssignableFrom(it.getClass()))
+                .map(Service.class::cast)
+                .findFirst()
+                .orElseThrow(() -> new RuntimeCamelException("Missing Service 
in Kubernetes manifest"));
+
+        Deployment deployment = resources.stream()
+                .filter(it -> Deployment.class.isAssignableFrom(it.getClass()))
+                .map(Deployment.class::cast)
+                .findFirst()
+                .orElseThrow(() -> new RuntimeCamelException("Missing 
deployment in Kubernetes manifest"));
+
+        var containers = 
deployment.getSpec().getTemplate().getSpec().getContainers();
+        Assertions.assertEquals(ContainerTrait.DEFAULT_CONTAINER_PORT_NAME, 
containers.get(0).getPorts().get(0).getName());
+        Assertions.assertEquals(ContainerTrait.DEFAULT_CONTAINER_PORT_NAME, 
svc.getSpec().getPorts().get(0).getName());
+    }
+
+    private void setupServerExpectsMinikube() {
+        Node nodeCR = new NodeBuilder()
+                .withNewMetadata()
+                .withName("minikube")
+                .withLabels(Collections.singletonMap("minikube.k8s.io/name", 
"minikube"))
+                .endMetadata()
+                .build();
+        NodeList nodeList = new NodeListBuilder().addToItems(nodeCR)
+                .build();
+        
server.expect().get().withPath("/api/v1/nodes?labelSelector=minikube.k8s.io%2Fname")
+                .andReturn(HttpURLConnection.HTTP_OK, nodeList)
+                .once();
+    }
+
+    private void setupServerExpectsOpenshift() {
+        ClusterVersion versionCR = new ClusterVersionBuilder()
+                .withNewMetadata().withName("version").endMetadata()
+                .withNewStatus()
+                .withNewDesired()
+                .withVersion("4.14.5")
+                .endDesired()
+                .endStatus()
+                .build();
+
+        
server.expect().get().withPath("/apis/config.openshift.io/v1/clusterversions/version")
+                .andReturn(HttpURLConnection.HTTP_OK, versionCR)
+                .once();
+    }
+
     private KubernetesRun createCommand(String[] files, String... args) {
         var argsArr = Optional.ofNullable(args).orElse(new String[0]);
         var argsLst = new ArrayList<>(Arrays.asList(argsArr));


Reply via email to