This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26240-kubernetes-crd-scale in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8142a62046dccd2f42c55bf15b35029f3dc618d9 Author: Claus Ibsen <[email protected]> AuthorDate: Tue Nov 3 15:31:15 2026 +0100 CAMEL-26240: camel-kubernetes - custom-resources uses the crd* options, scale returns the spec replicas, producers use the namespace option - custom-resources: the CRD context was built from the headers only, so the crd* endpoint options were checked but ignored; getCustomResource of a missing name failed (require() throws ResourceNotFoundException) instead of returning an empty JsonObject; a 409 Conflict on create/update was logged and swallowed; listCustomResourcesByLabels without labels failed with a NPE; the consumer logged "namespace is not specified" when it was set. - scaleDeployment and scaleReplicationController returned the status replicas (NPE without status); they now return the spec replicas. - listPersistentVolumesByLabels and listNodesByLabels without labels fail with an IllegalArgumentException instead of a NPE. - the namespaced kubernetes-* producers fall back to the namespace option when the CamelKubernetesNamespaceName header is not set. - secrets: the annotations header is set on a copy of the Secret header, and a Secret without metadata no longer fails with a NPE. - docs: updateService in the kubernetes-services operations, upgrade guide. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../components/kubernetes-custom-resources.json | 2 +- .../catalog/components/kubernetes-deployments.json | 2 +- .../kubernetes-replication-controllers.json | 2 +- .../docs/kubernetes-services-component.adoc | 1 + .../kubernetes-custom-resources.json | 2 +- .../deployments/kubernetes-deployments.json | 2 +- .../kubernetes-replication-controllers.json | 2 +- .../main/docs/kubernetes-services-component.adoc | 1 + .../component/kubernetes/KubernetesHelper.java | 16 ++ .../config_maps/KubernetesConfigMapsProducer.java | 11 +- .../cronjob/KubernetesCronJobProducer.java | 11 +- .../KubernetesCustomResourcesConsumer.java | 2 +- .../KubernetesCustomResourcesEndpoint.java | 6 +- .../KubernetesCustomResourcesProducer.java | 54 +++--- .../deployments/KubernetesDeploymentsEndpoint.java | 2 +- .../deployments/KubernetesDeploymentsProducer.java | 18 +- .../events/KubernetesEventsProducer.java | 11 +- .../kubernetes/hpa/KubernetesHPAProducer.java | 11 +- .../kubernetes/job/KubernetesJobProducer.java | 11 +- .../kubernetes/nodes/KubernetesNodesProducer.java | 4 + .../KubernetesPersistentVolumesProducer.java | 4 + .../KubernetesPersistentVolumesClaimsProducer.java | 11 +- .../kubernetes/pods/KubernetesPodsProducer.java | 11 +- .../KubernetesReplicationControllersEndpoint.java | 2 +- .../KubernetesReplicationControllersProducer.java | 17 +- .../KubernetesResourcesQuotaProducer.java | 11 +- .../secrets/KubernetesSecretsProducer.java | 16 +- .../KubernetesServiceAccountsProducer.java | 11 +- .../services/KubernetesServicesProducer.java | 11 +- .../KubernetesCustomResourcesProducerCrudTest.java | 186 +++++++++++++++++++ .../producer/KubernetesProducersCrudTest.java | 204 +++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 18 ++ 32 files changed, 559 insertions(+), 114 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-custom-resources.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-custom-resources.json index 029a197eaa44..46251e4c5e5c 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-custom-resources.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-custom-resources.json @@ -47,7 +47,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "format": "json", "description": "The custom resource that changed as JSON text, with the action (ADDED, MODIFIED or DELETED) in the CamelKubernetesCRDEventAction header" }, "producerIn": { "carriers": [ "any" ], "description": "Not used: the operations take their input from headers, such as the name of the instance in CamelKubernetesCRDInstanceName and, for createCustomResource and updateCustomResource, the instance as JSON text in CamelKubernetesCRDInstance" }, - "producerOut": { "carriers": [ "list", "map", "pojo", "unchanged" ], "byOption": [ { "option": "operation", "value": "listCustomResources", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "listCustomResourcesByLabels", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "getCustomResource", "carriers": [ "map" ], "javaType": "org.apache.camel.util.json.JsonObjec [...] + "producerOut": { "carriers": [ "list", "map", "pojo", "unchanged" ], "byOption": [ { "option": "operation", "value": "listCustomResources", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "listCustomResourcesByLabels", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "getCustomResource", "carriers": [ "map" ], "javaType": "org.apache.camel.util.json.JsonObjec [...] }, "properties": { "masterUrl": { "index": 0, "kind": "path", "displayName": "Master Url", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kubernetes.KubernetesConfiguration", "configurationField": "configuration", "description": "URL to a remote Kubernetes API server. This should only be used when your Camel application i [...] diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-deployments.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-deployments.json index 655d5368a161..0a46bdd9be01 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-deployments.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-deployments.json @@ -43,7 +43,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.apps.Deployment", "description": "The Deployment that changed, with the action (ADDED, MODIFIED or DELETED) in the CamelKubernetesEventAction header" }, "producerIn": { "carriers": [ "any" ], "description": "Not used: the operations take their input from headers, such as the name of the Deployment in CamelKubernetesDeploymentName, its DeploymentSpec in CamelKubernetesDeploymentSpec and the replicas of scaleDeployment in CamelKubernetesDeploymentReplicas" }, - "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeployments", "carriers": [ "list" ] }, { "option": "operation", "value": "listDeploymentsByLabels", "carriers": [ "list" ] }, { "option": "operation", "value": "getDeployment", "carriers": [ "pojo", "null" ] }, { "option": "operation", "value": "createDeployment", "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.apps.Deployment" }, { "option": "o [...] + "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeployments", "carriers": [ "list" ] }, { "option": "operation", "value": "listDeploymentsByLabels", "carriers": [ "list" ] }, { "option": "operation", "value": "getDeployment", "carriers": [ "pojo", "null" ] }, { "option": "operation", "value": "createDeployment", "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.apps.Deployment" }, { "option": "o [...] }, "properties": { "masterUrl": { "index": 0, "kind": "path", "displayName": "Master Url", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kubernetes.KubernetesConfiguration", "configurationField": "configuration", "description": "URL to a remote Kubernetes API server. This should only be used when your Camel application i [...] diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-replication-controllers.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-replication-controllers.json index f328bc09d4b9..26de7fd36d05 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-replication-controllers.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kubernetes-replication-controllers.json @@ -42,7 +42,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.ReplicationController", "description": "The ReplicationController that changed, one message per watch event, with the action (ADDED, MODIFIED, DELETED) in the CamelKubernetesEventAction header" }, "producerIn": { "carriers": [ "any" ], "description": "Not used: the operation (the operation option, or else the CamelKubernetesOperation header) takes its input from headers: the namespace in CamelKubernetesNamespaceName, the name in CamelKubernetesReplicationControllerName, the labels in CamelKubernetesReplicationControllersLabels, the ReplicationControllerSpec in CamelKubernetesReplicationControllerSpec and the replicas in CamelKubernetesReplicationControllerReplicas" }, - "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listReplicationControllers", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "listReplicationControllersByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "getReplicationC [...] + "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listReplicationControllers", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "listReplicationControllersByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "getReplicationC [...] }, "properties": { "masterUrl": { "index": 0, "kind": "path", "displayName": "Master Url", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kubernetes.KubernetesConfiguration", "configurationField": "configuration", "description": "URL to a remote Kubernetes API server. This should only be used when your Camel application i [...] diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-services-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-services-component.adoc index cb2fd666d775..3793f677cd3a 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-services-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-services-component.adoc @@ -33,6 +33,7 @@ include::partial$component-endpoint-headers.adoc[] - `listServicesByLabels` - `getService` - `createService` +- `updateService` - `deleteService` == Examples diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/customresources/kubernetes-custom-resources.json b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/customresources/kubernetes-custom-resources.json index 029a197eaa44..46251e4c5e5c 100644 --- a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/customresources/kubernetes-custom-resources.json +++ b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/customresources/kubernetes-custom-resources.json @@ -47,7 +47,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", "format": "json", "description": "The custom resource that changed as JSON text, with the action (ADDED, MODIFIED or DELETED) in the CamelKubernetesCRDEventAction header" }, "producerIn": { "carriers": [ "any" ], "description": "Not used: the operations take their input from headers, such as the name of the instance in CamelKubernetesCRDInstanceName and, for createCustomResource and updateCustomResource, the instance as JSON text in CamelKubernetesCRDInstance" }, - "producerOut": { "carriers": [ "list", "map", "pojo", "unchanged" ], "byOption": [ { "option": "operation", "value": "listCustomResources", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "listCustomResourcesByLabels", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "getCustomResource", "carriers": [ "map" ], "javaType": "org.apache.camel.util.json.JsonObjec [...] + "producerOut": { "carriers": [ "list", "map", "pojo", "unchanged" ], "byOption": [ { "option": "operation", "value": "listCustomResources", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "listCustomResourcesByLabels", "carriers": [ "list" ], "javaType": "org.apache.camel.util.json.JsonArray" }, { "option": "operation", "value": "getCustomResource", "carriers": [ "map" ], "javaType": "org.apache.camel.util.json.JsonObjec [...] }, "properties": { "masterUrl": { "index": 0, "kind": "path", "displayName": "Master Url", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kubernetes.KubernetesConfiguration", "configurationField": "configuration", "description": "URL to a remote Kubernetes API server. This should only be used when your Camel application i [...] diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/deployments/kubernetes-deployments.json b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/deployments/kubernetes-deployments.json index 655d5368a161..0a46bdd9be01 100644 --- a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/deployments/kubernetes-deployments.json +++ b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/deployments/kubernetes-deployments.json @@ -43,7 +43,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.apps.Deployment", "description": "The Deployment that changed, with the action (ADDED, MODIFIED or DELETED) in the CamelKubernetesEventAction header" }, "producerIn": { "carriers": [ "any" ], "description": "Not used: the operations take their input from headers, such as the name of the Deployment in CamelKubernetesDeploymentName, its DeploymentSpec in CamelKubernetesDeploymentSpec and the replicas of scaleDeployment in CamelKubernetesDeploymentReplicas" }, - "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeployments", "carriers": [ "list" ] }, { "option": "operation", "value": "listDeploymentsByLabels", "carriers": [ "list" ] }, { "option": "operation", "value": "getDeployment", "carriers": [ "pojo", "null" ] }, { "option": "operation", "value": "createDeployment", "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.apps.Deployment" }, { "option": "o [...] + "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeployments", "carriers": [ "list" ] }, { "option": "operation", "value": "listDeploymentsByLabels", "carriers": [ "list" ] }, { "option": "operation", "value": "getDeployment", "carriers": [ "pojo", "null" ] }, { "option": "operation", "value": "createDeployment", "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.apps.Deployment" }, { "option": "o [...] }, "properties": { "masterUrl": { "index": 0, "kind": "path", "displayName": "Master Url", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kubernetes.KubernetesConfiguration", "configurationField": "configuration", "description": "URL to a remote Kubernetes API server. This should only be used when your Camel application i [...] diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/replication_controllers/kubernetes-replication-controllers.json b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/replication_controllers/kubernetes-replication-controllers.json index f328bc09d4b9..26de7fd36d05 100644 --- a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/replication_controllers/kubernetes-replication-controllers.json +++ b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/kubernetes/replication_controllers/kubernetes-replication-controllers.json @@ -42,7 +42,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "io.fabric8.kubernetes.api.model.ReplicationController", "description": "The ReplicationController that changed, one message per watch event, with the action (ADDED, MODIFIED, DELETED) in the CamelKubernetesEventAction header" }, "producerIn": { "carriers": [ "any" ], "description": "Not used: the operation (the operation option, or else the CamelKubernetesOperation header) takes its input from headers: the namespace in CamelKubernetesNamespaceName, the name in CamelKubernetesReplicationControllerName, the labels in CamelKubernetesReplicationControllersLabels, the ReplicationControllerSpec in CamelKubernetesReplicationControllerSpec and the replicas in CamelKubernetesReplicationControllerReplicas" }, - "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listReplicationControllers", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "listReplicationControllersByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "getReplicationC [...] + "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listReplicationControllers", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "listReplicationControllersByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.kubernetes.api.model.ReplicationController>" }, { "option": "operation", "value": "getReplicationC [...] }, "properties": { "masterUrl": { "index": 0, "kind": "path", "displayName": "Master Url", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.kubernetes.KubernetesConfiguration", "configurationField": "configuration", "description": "URL to a remote Kubernetes API server. This should only be used when your Camel application i [...] diff --git a/components/camel-kubernetes/src/main/docs/kubernetes-services-component.adoc b/components/camel-kubernetes/src/main/docs/kubernetes-services-component.adoc index cb2fd666d775..3793f677cd3a 100644 --- a/components/camel-kubernetes/src/main/docs/kubernetes-services-component.adoc +++ b/components/camel-kubernetes/src/main/docs/kubernetes-services-component.adoc @@ -33,6 +33,7 @@ include::partial$component-endpoint-headers.adoc[] - `listServicesByLabels` - `getService` - `createService` +- `updateService` - `deleteService` == Examples diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesHelper.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesHelper.java index db7e3af04f26..8afec60646b5 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesHelper.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/KubernetesHelper.java @@ -204,6 +204,22 @@ public final class KubernetesHelper { return operation; } + /** + * The namespace of a producer operation: the CamelKubernetesNamespaceName header, or else the namespace option of + * the endpoint. + * + * @param endpoint the endpoint of the producer + * @param exchange the exchange + * @return the namespace, or null when neither the header nor the option is set + */ + public static String extractNamespace(AbstractKubernetesEndpoint endpoint, Exchange exchange) { + String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + if (ObjectHelper.isEmpty(namespace)) { + namespace = endpoint.getKubernetesConfiguration().getNamespace(); + } + return namespace; + } + public static void prepareOutboundMessage(Exchange exchange, Object body) { MessageHelper.copyHeaders(exchange.getIn(), exchange.getMessage(), true); exchange.getMessage().setBody(body); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java index c6a6a3837883..06f42f1a54b8 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/config_maps/KubernetesConfigMapsProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesConfigMapsProducer extends DefaultProducer { @@ -87,7 +88,7 @@ public class KubernetesConfigMapsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); ConfigMapList configMapsList; if (ObjectHelper.isEmpty(namespace)) { @@ -100,7 +101,7 @@ public class KubernetesConfigMapsProducer extends DefaultProducer { } protected void doListConfigMapsByLabels(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CONFIGMAPS_LABELS, Map.class); ConfigMapList configMapsList; @@ -120,7 +121,7 @@ public class KubernetesConfigMapsProducer extends DefaultProducer { protected void doGetConfigMap(Exchange exchange) { String cfMapName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CONFIGMAP_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(cfMapName)) { LOG.error("Get a specific ConfigMap require specify a ConfigMap name"); throw new IllegalArgumentException("Get a specific ConfigMap require specify a ConfigMap name"); @@ -146,7 +147,7 @@ public class KubernetesConfigMapsProducer extends DefaultProducer { private void doCreateOrUpdateConfigMap( Exchange exchange, String operationName, Function<Resource<ConfigMap>, ConfigMap> operation) { String cfMapName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CONFIGMAP_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); HashMap<String, String> configMapData = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CONFIGMAP_DATA, HashMap.class); HashMap<String, String> configMapAnnotations @@ -184,7 +185,7 @@ public class KubernetesConfigMapsProducer extends DefaultProducer { protected void doDeleteConfigMap(Exchange exchange) { String configMapName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CONFIGMAP_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(configMapName)) { LOG.error("Delete a specific config map require specify a config map name"); throw new IllegalArgumentException("Delete a specific config map require specify a config map name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/cronjob/KubernetesCronJobProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/cronjob/KubernetesCronJobProducer.java index 1b4473a18f25..4b78a5991a2e 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/cronjob/KubernetesCronJobProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/cronjob/KubernetesCronJobProducer.java @@ -35,6 +35,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesCronJobProducer extends DefaultProducer { @@ -92,7 +93,7 @@ public class KubernetesCronJobProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); CronJobList cronJobList; if (ObjectHelper.isEmpty(namespace)) { @@ -105,7 +106,7 @@ public class KubernetesCronJobProducer extends DefaultProducer { } protected void doListCronJobByLabel(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRON_JOB_LABELS, Map.class); CronJobList cronJobList; @@ -127,7 +128,7 @@ public class KubernetesCronJobProducer extends DefaultProducer { protected void doGetCronJob(Exchange exchange) { String cronjobName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRON_JOB_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(cronjobName)) { throw new IllegalArgumentException("Get a specific cronjob require specify a cronnjob name"); } @@ -151,7 +152,7 @@ public class KubernetesCronJobProducer extends DefaultProducer { private void doCreateOrUpdateCronJob( Exchange exchange, String operationName, Function<Resource<CronJob>, Object> operation) { String cronjobName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRON_JOB_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); CronJobSpec cronjobSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRON_JOB_SPEC, CronJobSpec.class); HashMap<String, String> annotations = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRON_JOB_ANNOTATIONS, HashMap.class); @@ -185,7 +186,7 @@ public class KubernetesCronJobProducer extends DefaultProducer { protected void doDeleteCronJob(Exchange exchange) { String cronjobName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRON_JOB_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(cronjobName)) { throw new IllegalArgumentException("Delete a specific cronjob require specify a cronjob name"); } diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesConsumer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesConsumer.java index 726b1a2fb113..cb93edfe5b6b 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesConsumer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesConsumer.java @@ -84,7 +84,7 @@ public class KubernetesCustomResourcesConsumer extends DefaultConsumer { @Override public void run() { - if (ObjectHelper.isNotEmpty(getEndpoint().getKubernetesConfiguration().getNamespace())) { + if (ObjectHelper.isEmpty(getEndpoint().getKubernetesConfiguration().getNamespace())) { LOG.error("namespace is not specified."); } String namespace = getEndpoint().getKubernetesConfiguration().getNamespace(); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesEndpoint.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesEndpoint.java index efcee81ca56c..563301f39bac 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesEndpoint.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesEndpoint.java @@ -63,9 +63,9 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_C @BodyType.When(option = "operation", value = "deleteCustomResource", carriers = Carrier.UNCHANGED) }, description = "A JsonArray of the GenericKubernetesResource of the instances for the list" - + " operations, a JsonObject of the instance for getCustomResource, and the" - + " GenericKubernetesResource of create and update (an empty one when it already" - + " exists). deleteCustomResource leaves the body as it was, with whether it deleted" + + " operations, a JsonObject of the instance for getCustomResource (an empty one when" + + " it is not found), and the GenericKubernetesResource of create and update." + + " deleteCustomResource leaves the body as it was, with whether it deleted" + " the instance in the CamelKubernetesDeleteResult header")) public class KubernetesCustomResourcesEndpoint extends AbstractKubernetesEndpoint { diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesProducer.java index 8377a8325b79..a4d1152f2765 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/customresources/KubernetesCustomResourcesProducer.java @@ -124,6 +124,10 @@ public class KubernetesCustomResourcesProducer extends DefaultProducer { protected void doListByLabels(Exchange exchange, String namespaceName) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRD_LABELS, Map.class); + if (ObjectHelper.isEmpty(labels)) { + LOG.error("Get custom resources by labels require specify a labels set"); + throw new IllegalArgumentException("Get custom resources by labels require specify a labels set"); + } GenericKubernetesResourceList list = getEndpoint().getKubernetesClient() .genericKubernetesResources(getCRDContext(exchange.getIn())) .inNamespace(namespaceName) @@ -144,20 +148,15 @@ public class KubernetesCustomResourcesProducer extends DefaultProducer { throw new IllegalArgumentException("Get a specific custom resource require specify a custom resource name"); } JsonObject customResourceJSON = new JsonObject(); - try { - customResourceJSON = new JsonObject( - getEndpoint().getKubernetesClient().genericKubernetesResources(getCRDContext(exchange.getIn())) - .inNamespace(namespaceName) - .withName(customResourceName) - .require() - .get()); - - } catch (KubernetesClientException e) { - if (e.getCode() == 404) { - LOG.info("Custom resource instance not found", e); - } else { - throw e; - } + GenericKubernetesResource customResource + = getEndpoint().getKubernetesClient().genericKubernetesResources(getCRDContext(exchange.getIn())) + .inNamespace(namespaceName) + .withName(customResourceName) + .get(); + if (customResource != null) { + customResourceJSON = new JsonObject(customResource.get()); + } else { + LOG.debug("Custom resource instance {} not found in namespace {}", customResourceName, namespaceName); } prepareOutboundMessage(exchange, customResourceJSON); @@ -199,19 +198,10 @@ public class KubernetesCustomResourcesProducer extends DefaultProducer { Exchange exchange, String namespaceName, Function<Resource<GenericKubernetesResource>, GenericKubernetesResource> operation) { String customResourceInstance = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE, String.class); - GenericKubernetesResource customResource = new GenericKubernetesResource(); - try { - customResource = operation.apply(getEndpoint().getKubernetesClient() - .genericKubernetesResources(getCRDContext(exchange.getIn())) - .inNamespace(namespaceName) - .resource(Serialization.unmarshal(customResourceInstance, GenericKubernetesResource.class))); - } catch (KubernetesClientException e) { - if (e.getCode() == 409) { - LOG.info("Custom resource instance already exists", e); - } else { - throw e; - } - } + GenericKubernetesResource customResource = operation.apply(getEndpoint().getKubernetesClient() + .genericKubernetesResources(getCRDContext(exchange.getIn())) + .inNamespace(namespaceName) + .resource(Serialization.unmarshal(customResourceInstance, GenericKubernetesResource.class))); prepareOutboundMessage(exchange, customResource); } @@ -255,11 +245,11 @@ public class KubernetesCustomResourcesProducer extends DefaultProducer { } return new CustomResourceDefinitionContext.Builder() - .withName(message.getHeader(KubernetesConstants.KUBERNETES_CRD_NAME, String.class)) // example: "githubsources.sources.knative.dev" - .withGroup(message.getHeader(KubernetesConstants.KUBERNETES_CRD_GROUP, String.class)) // example: "sources.knative.dev" - .withScope(message.getHeader(KubernetesConstants.KUBERNETES_CRD_SCOPE, String.class)) // example: "Namespaced" - .withVersion(message.getHeader(KubernetesConstants.KUBERNETES_CRD_VERSION, String.class)) // example: "v1alpha1" - .withPlural(message.getHeader(KubernetesConstants.KUBERNETES_CRD_PLURAL, String.class)) // example: "githubsources" + .withName(name) // example: "githubsources.sources.knative.dev" + .withGroup(group) // example: "sources.knative.dev" + .withScope(scope) // example: "Namespaced" + .withVersion(version) // example: "v1alpha1" + .withPlural(plural) // example: "githubsources" .build(); } } diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsEndpoint.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsEndpoint.java index 94835ba279aa..298915d955dc 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsEndpoint.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsEndpoint.java @@ -64,7 +64,7 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_D carriers = Carrier.VALUE, javaType = "java.lang.Integer") }, description = "A List of the Deployments for the list operations, the Deployment of get, create and " + "update (null when getDeployment finds none), and a Boolean of whether deleteDeployment " - + "deleted it. scaleDeployment gives the number of replicas in the status of the " + + "deleted it. scaleDeployment gives the number of replicas in the spec of the scaled " + "Deployment")) public class KubernetesDeploymentsEndpoint extends AbstractKubernetesEndpoint { diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsProducer.java index e1d5b5a0acba..d32ce706750e 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/deployments/KubernetesDeploymentsProducer.java @@ -38,6 +38,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesDeploymentsProducer extends DefaultProducer { @@ -93,7 +94,7 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); DeploymentList deploymentsList; if (ObjectHelper.isEmpty(namespace)) { @@ -116,7 +117,7 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { throw new IllegalArgumentException("Listing Deployments by labels requires specifying labels"); } - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); DeploymentList deploymentsList; if (ObjectHelper.isNotEmpty(namespace)) { @@ -130,7 +131,7 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { protected void doGetDeployment(Exchange exchange) { String deploymentName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, String.class); - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(deploymentName)) { LOG.error("Get a specific Deployment require specify a Deployment name"); throw new IllegalArgumentException("Get a specific Deployment require specify a Deployment name"); @@ -148,7 +149,7 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { protected void doDeleteDeployment(Exchange exchange) { String deploymentName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(deploymentName)) { LOG.error("Delete a specific deployment require specify a deployment name"); throw new IllegalArgumentException("Delete a specific deployment require specify a deployment name"); @@ -176,7 +177,7 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { private void doCreateOrUpdateDeployment( Exchange exchange, String operationName, Function<Resource<Deployment>, Deployment> operation) { String deploymentName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); DeploymentSpec deSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_SPEC, DeploymentSpec.class); if (ObjectHelper.isEmpty(deploymentName)) { @@ -215,7 +216,7 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { protected void doScaleDeployment(Exchange exchange) { String deploymentName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); Integer replicasNumber = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_REPLICAS, Integer.class); if (ObjectHelper.isEmpty(deploymentName)) { LOG.error("Scale a specific deployment require specify a deployment name"); @@ -232,6 +233,9 @@ public class KubernetesDeploymentsProducer extends DefaultProducer { Deployment deploymentScaled = getEndpoint().getKubernetesClient().apps().deployments().inNamespace(namespaceName) .withName(deploymentName).scale(replicasNumber); - prepareOutboundMessage(exchange, deploymentScaled.getStatus().getReplicas()); + // the replicas of the spec: the status may not be there yet, and lags behind until the pods are started or stopped + Integer replicas = deploymentScaled != null && deploymentScaled.getSpec() != null + ? deploymentScaled.getSpec().getReplicas() : null; + prepareOutboundMessage(exchange, replicas != null ? replicas : replicasNumber); } } diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/events/KubernetesEventsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/events/KubernetesEventsProducer.java index 3a65f05e3d6e..2d6a44d2ba00 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/events/KubernetesEventsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/events/KubernetesEventsProducer.java @@ -39,6 +39,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesEventsProducer extends DefaultProducer { @@ -91,7 +92,7 @@ public class KubernetesEventsProducer extends DefaultProducer { protected void doList(Exchange exchange) { EventList eventList; - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(namespace)) { eventList = getEndpoint().getKubernetesClient().events().v1().events().inAnyNamespace().list(); } else { @@ -101,7 +102,7 @@ public class KubernetesEventsProducer extends DefaultProducer { } protected void doListEventsByLabel(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_EVENTS_LABELS, Map.class); EventList eventList; @@ -122,7 +123,7 @@ public class KubernetesEventsProducer extends DefaultProducer { protected void doGetEvent(Exchange exchange) { String eventName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_EVENT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(eventName)) { LOG.error("Get a specific event require specify a event name"); throw new IllegalArgumentException("Get a specific event require specify a event name"); @@ -147,7 +148,7 @@ public class KubernetesEventsProducer extends DefaultProducer { private void doCreateOrUpdateEvent(Exchange exchange, String operationName, Function<Resource<Event>, Event> operation) { String eventName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_EVENT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(eventName)) { LOG.error("{} a specific event require specify a event name", operationName); throw new IllegalArgumentException( @@ -189,7 +190,7 @@ public class KubernetesEventsProducer extends DefaultProducer { protected void doDeleteEvent(Exchange exchange) { String eventName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_EVENT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(eventName)) { LOG.error("Delete a specific event require specify a event name"); throw new IllegalArgumentException("Delete a specific event require specify a event name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/hpa/KubernetesHPAProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/hpa/KubernetesHPAProducer.java index 5f9167914d63..18aaeaab7aae 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/hpa/KubernetesHPAProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/hpa/KubernetesHPAProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesHPAProducer extends DefaultProducer { @@ -87,7 +88,7 @@ public class KubernetesHPAProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); HorizontalPodAutoscalerList hpaList; if (ObjectHelper.isEmpty(namespace)) { @@ -101,7 +102,7 @@ public class KubernetesHPAProducer extends DefaultProducer { } protected void doListHPAByLabel(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_HPA_LABELS, Map.class); HorizontalPodAutoscalerList hpaList; @@ -123,7 +124,7 @@ public class KubernetesHPAProducer extends DefaultProducer { protected void doGetHPA(Exchange exchange) { String podName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_HPA_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(podName)) { LOG.error("Get a specific hpa require specify an hpa name"); throw new IllegalArgumentException("Get a specific hpa require specify an hpa name"); @@ -151,7 +152,7 @@ public class KubernetesHPAProducer extends DefaultProducer { Exchange exchange, String operationName, Function<Resource<HorizontalPodAutoscaler>, HorizontalPodAutoscaler> operation) { String hpaName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_HPA_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); HorizontalPodAutoscalerSpec hpaSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_HPA_SPEC, HorizontalPodAutoscalerSpec.class); if (ObjectHelper.isEmpty(hpaName)) { @@ -179,7 +180,7 @@ public class KubernetesHPAProducer extends DefaultProducer { protected void doDeleteHPA(Exchange exchange) { String hpaName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_HPA_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(hpaName)) { LOG.error("Delete a specific hpa require specify a hpa name"); throw new IllegalArgumentException("Delete a specific hpa require specify a hpa name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/job/KubernetesJobProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/job/KubernetesJobProducer.java index a60b6261e9b5..776f2b3c1f2d 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/job/KubernetesJobProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/job/KubernetesJobProducer.java @@ -35,6 +35,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesJobProducer extends DefaultProducer { @@ -92,7 +93,7 @@ public class KubernetesJobProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); JobList jobList; if (ObjectHelper.isEmpty(namespace)) { @@ -105,7 +106,7 @@ public class KubernetesJobProducer extends DefaultProducer { } protected void doListJobByLabel(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_JOB_LABELS, Map.class); JobList jobList; @@ -125,7 +126,7 @@ public class KubernetesJobProducer extends DefaultProducer { protected void doGetJob(Exchange exchange) { String jobName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_JOB_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(jobName)) { LOG.error("Get a specific job require specify a job name"); throw new IllegalArgumentException("Get a specific job require specify a job name"); @@ -149,7 +150,7 @@ public class KubernetesJobProducer extends DefaultProducer { private void doCreateOrUpdateJob(Exchange exchange, String operationName, Function<Resource<Job>, Job> operation) { String jobName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_JOB_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); JobSpec jobSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_JOB_SPEC, JobSpec.class); HashMap<String, String> annotations = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_JOB_ANNOTATIONS, HashMap.class); @@ -185,7 +186,7 @@ public class KubernetesJobProducer extends DefaultProducer { protected void doDeleteJob(Exchange exchange) { String jobName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_JOB_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(jobName)) { LOG.error("Delete a specific job require specify a job name"); throw new IllegalArgumentException("Delete a specific job require specify a job name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/nodes/KubernetesNodesProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/nodes/KubernetesNodesProducer.java index c54403d498c7..e65590ea6546 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/nodes/KubernetesNodesProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/nodes/KubernetesNodesProducer.java @@ -94,6 +94,10 @@ public class KubernetesNodesProducer extends DefaultProducer { protected void doListNodesByLabels(Exchange exchange) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NODES_LABELS, Map.class); + if (ObjectHelper.isEmpty(labels)) { + LOG.error("Get nodes by labels require specify a labels set"); + throw new IllegalArgumentException("Get nodes by labels require specify a labels set"); + } NodeList nodeList = getEndpoint().getKubernetesClient() .nodes() .withLabels(labels) diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes/KubernetesPersistentVolumesProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes/KubernetesPersistentVolumesProducer.java index bea1242e2684..31335db62d60 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes/KubernetesPersistentVolumesProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes/KubernetesPersistentVolumesProducer.java @@ -77,6 +77,10 @@ public class KubernetesPersistentVolumesProducer extends DefaultProducer { protected void doListPersistentVolumesByLabels(Exchange exchange) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_PERSISTENT_VOLUMES_LABELS, Map.class); + if (ObjectHelper.isEmpty(labels)) { + LOG.error("Get Persistent Volumes by labels require specify a labels set"); + throw new IllegalArgumentException("Get Persistent Volumes by labels require specify a labels set"); + } PersistentVolumeList pvList = getEndpoint().getKubernetesClient().persistentVolumes().withLabels(labels).list(); prepareOutboundMessage(exchange, pvList.getItems()); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes_claims/KubernetesPersistentVolumesClaimsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes_claims/KubernetesPersistentVolumesClaimsProducer.java index 6376a9260d63..da9ec24fac89 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes_claims/KubernetesPersistentVolumesClaimsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/persistent_volumes_claims/KubernetesPersistentVolumesClaimsProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesPersistentVolumesClaimsProducer extends DefaultProducer { @@ -87,7 +88,7 @@ public class KubernetesPersistentVolumesClaimsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); PersistentVolumeClaimList pvcList; if (ObjectHelper.isEmpty(namespace)) { @@ -100,7 +101,7 @@ public class KubernetesPersistentVolumesClaimsProducer extends DefaultProducer { } protected void doListPersistentVolumesClaimsByLabels(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_PERSISTENT_VOLUMES_CLAIMS_LABELS, Map.class); PersistentVolumeClaimList pvcList; @@ -122,7 +123,7 @@ public class KubernetesPersistentVolumesClaimsProducer extends DefaultProducer { protected void doGetPersistentVolumeClaim(Exchange exchange) { String pvcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_PERSISTENT_VOLUME_CLAIM_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(pvcName)) { LOG.error("Get a specific Persistent Volume Claim require specify a Persistent Volume Claim name"); throw new IllegalArgumentException( @@ -150,7 +151,7 @@ public class KubernetesPersistentVolumesClaimsProducer extends DefaultProducer { Exchange exchange, String operationName, Function<Resource<PersistentVolumeClaim>, PersistentVolumeClaim> operation) { String pvcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_PERSISTENT_VOLUME_CLAIM_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); PersistentVolumeClaimSpec pvcSpec = exchange.getIn() .getHeader(KubernetesConstants.KUBERNETES_PERSISTENT_VOLUME_CLAIM_SPEC, PersistentVolumeClaimSpec.class); if (ObjectHelper.isEmpty(pvcName)) { @@ -184,7 +185,7 @@ public class KubernetesPersistentVolumesClaimsProducer extends DefaultProducer { protected void doDeletePersistentVolumeClaim(Exchange exchange) { String pvcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_PERSISTENT_VOLUME_CLAIM_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(pvcName)) { LOG.error("Delete a specific Persistent Volume Claim require specify a Persistent Volume Claim name"); throw new IllegalArgumentException( diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/pods/KubernetesPodsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/pods/KubernetesPodsProducer.java index 7a95bf151cf3..e12813720183 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/pods/KubernetesPodsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/pods/KubernetesPodsProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesPodsProducer extends DefaultProducer { @@ -87,7 +88,7 @@ public class KubernetesPodsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); PodList podList; if (ObjectHelper.isEmpty(namespace)) { @@ -100,7 +101,7 @@ public class KubernetesPodsProducer extends DefaultProducer { } protected void doListPodsByLabel(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_PODS_LABELS, Map.class); PodList podList; @@ -120,7 +121,7 @@ public class KubernetesPodsProducer extends DefaultProducer { protected void doGetPod(Exchange exchange) { String podName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_POD_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(podName)) { LOG.error("Get a specific pod require specify a pod name"); throw new IllegalArgumentException("Get a specific pod require specify a pod name"); @@ -144,7 +145,7 @@ public class KubernetesPodsProducer extends DefaultProducer { private void doCreateOrUpdatePod(Exchange exchange, String operationName, Function<Resource<Pod>, Pod> operation) { String podName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_POD_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); PodSpec podSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_POD_SPEC, PodSpec.class); if (ObjectHelper.isEmpty(podName)) { LOG.error("{} a specific pod require specify a pod name", operationName); @@ -170,7 +171,7 @@ public class KubernetesPodsProducer extends DefaultProducer { protected void doDeletePod(Exchange exchange) { String podName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_POD_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(podName)) { LOG.error("Delete a specific pod require specify a pod name"); throw new IllegalArgumentException("Delete a specific pod require specify a pod name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersEndpoint.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersEndpoint.java index 83a2170553de..e5c51fb46bc3 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersEndpoint.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersEndpoint.java @@ -76,7 +76,7 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_R + " getReplicationController (null when not found)," + " createReplicationController and updateReplicationController; a Boolean" + " for deleteReplicationController, false when not found; for" - + " scaleReplicationController the replicas of its status, as an Integer")) + + " scaleReplicationController the replicas of its spec, as an Integer")) public class KubernetesReplicationControllersEndpoint extends AbstractKubernetesEndpoint { public KubernetesReplicationControllersEndpoint(String uri, KubernetesReplicationControllersComponent component, diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersProducer.java index 83e284e82a49..c8a3fdf1cc3b 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/replication_controllers/KubernetesReplicationControllersProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesReplicationControllersProducer extends DefaultProducer { @@ -91,7 +92,7 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); ReplicationControllerList rcList; if (ObjectHelper.isEmpty(namespace)) { rcList = getEndpoint().getKubernetesClient().replicationControllers().inAnyNamespace().list(); @@ -103,7 +104,7 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { } protected void doListReplicationControllersByLabels(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLERS_LABELS, Map.class); ReplicationControllerList rcList; @@ -125,7 +126,7 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { protected void doGetReplicationController(Exchange exchange) { String rcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(rcName)) { LOG.error("Get a specific replication controller require specify a replication controller name"); throw new IllegalArgumentException( @@ -153,7 +154,7 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { Exchange exchange, String operationName, Function<Resource<ReplicationController>, ReplicationController> operation) { String rcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); ReplicationControllerSpec rcSpec = exchange.getIn() .getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_SPEC, ReplicationControllerSpec.class); if (ObjectHelper.isEmpty(rcName)) { @@ -186,7 +187,7 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { protected void doDeleteReplicationController(Exchange exchange) { String rcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(rcName)) { LOG.error("Delete a specific replication controller require specify a replication controller name"); throw new IllegalArgumentException( @@ -207,7 +208,7 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { protected void doScaleReplicationController(Exchange exchange) { String rcName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); Integer replicasNumber = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_REPLICAS, Integer.class); if (ObjectHelper.isEmpty(rcName)) { @@ -226,6 +227,8 @@ public class KubernetesReplicationControllersProducer extends DefaultProducer { ReplicationController rcScaled = getEndpoint().getKubernetesClient().replicationControllers().inNamespace(namespaceName) .withName(rcName).scale(replicasNumber); - prepareOutboundMessage(exchange, rcScaled.getStatus().getReplicas()); + // the replicas of the spec: the status may not be there yet, and lags behind until the pods are started or stopped + Integer replicas = rcScaled != null && rcScaled.getSpec() != null ? rcScaled.getSpec().getReplicas() : null; + prepareOutboundMessage(exchange, replicas != null ? replicas : replicasNumber); } } diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/resources_quota/KubernetesResourcesQuotaProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/resources_quota/KubernetesResourcesQuotaProducer.java index 7d1eecdc79b6..ddbd44403961 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/resources_quota/KubernetesResourcesQuotaProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/resources_quota/KubernetesResourcesQuotaProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesResourcesQuotaProducer extends DefaultProducer { @@ -87,7 +88,7 @@ public class KubernetesResourcesQuotaProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); ResourceQuotaList resList; if (ObjectHelper.isEmpty(namespace)) { @@ -100,7 +101,7 @@ public class KubernetesResourcesQuotaProducer extends DefaultProducer { } protected void doListResourceQuotasByLabels(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_RESOURCES_QUOTA_LABELS, Map.class); ResourceQuotaList resList; @@ -121,7 +122,7 @@ public class KubernetesResourcesQuotaProducer extends DefaultProducer { protected void doGetResourceQuota(Exchange exchange) { String rqName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_RESOURCES_QUOTA_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(rqName)) { LOG.error("Get a specific Resource Quota require specify a Resource Quota name"); throw new IllegalArgumentException("Get a specific Resource Quota require specify a Resource Quota name"); @@ -147,7 +148,7 @@ public class KubernetesResourcesQuotaProducer extends DefaultProducer { private void doCreateOrUpdateResourceQuota( Exchange exchange, String operationName, Function<Resource<ResourceQuota>, ResourceQuota> operation) { String rqName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_RESOURCES_QUOTA_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); ResourceQuotaSpec rqSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_RESOURCE_QUOTA_SPEC, ResourceQuotaSpec.class); if (ObjectHelper.isEmpty(rqName)) { @@ -177,7 +178,7 @@ public class KubernetesResourcesQuotaProducer extends DefaultProducer { protected void doDeleteResourceQuota(Exchange exchange) { String rqName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_RESOURCES_QUOTA_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(rqName)) { LOG.error("Delete a specific resource quota require specify a resource quota name"); throw new IllegalArgumentException("Delete a specific resource quota require specify a resource quota name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/KubernetesSecretsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/KubernetesSecretsProducer.java index affa04104d3c..900bbab39483 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/KubernetesSecretsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/secrets/KubernetesSecretsProducer.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.function.Function; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; import io.fabric8.kubernetes.api.model.SecretList; import io.fabric8.kubernetes.api.model.StatusDetails; import io.fabric8.kubernetes.client.dsl.Resource; @@ -35,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesSecretsProducer extends DefaultProducer { @@ -86,7 +88,7 @@ public class KubernetesSecretsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); SecretList secretsList; if (ObjectHelper.isEmpty(namespace)) { @@ -100,7 +102,7 @@ public class KubernetesSecretsProducer extends DefaultProducer { protected void doListSecretsByLabels(Exchange exchange) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SECRETS_LABELS, Map.class); - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); SecretList secretsList; if (ObjectHelper.isEmpty(labels)) { @@ -119,7 +121,7 @@ public class KubernetesSecretsProducer extends DefaultProducer { protected void doGetSecret(Exchange exchange) { String secretName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SECRET_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(secretName)) { LOG.error("Get a specific Secret require specify a Secret name"); throw new IllegalArgumentException("Get a specific Secret require specify a Secret name"); @@ -142,7 +144,7 @@ public class KubernetesSecretsProducer extends DefaultProducer { } private void doCreateOrUpdateSecret(Exchange exchange, String operationName, Function<Resource<Secret>, Secret> operation) { - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); Secret secretToCreate = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SECRET, Secret.class); HashMap<String, String> secretAnnotations = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SECRETS_ANNOTATIONS, HashMap.class); @@ -157,7 +159,9 @@ public class KubernetesSecretsProducer extends DefaultProducer { String.format("%s a specific secret require specify a secret bean", operationName)); } if (ObjectHelper.isNotEmpty(secretAnnotations)) { - secretToCreate.getMetadata().setAnnotations(secretAnnotations); + // on a copy, so that the Secret of the header is not changed + secretToCreate = new SecretBuilder(secretToCreate).editOrNewMetadata().withAnnotations(secretAnnotations) + .endMetadata().build(); } Secret secret = operation.apply( @@ -168,7 +172,7 @@ public class KubernetesSecretsProducer extends DefaultProducer { protected void doDeleteSecret(Exchange exchange) { String secretName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SECRET_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(secretName)) { LOG.error("Delete a specific secret require specify a secret name"); throw new IllegalArgumentException("Delete a specific secret require specify a secret name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/service_accounts/KubernetesServiceAccountsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/service_accounts/KubernetesServiceAccountsProducer.java index 79f0cb4d65c2..6827d1bbbcfc 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/service_accounts/KubernetesServiceAccountsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/service_accounts/KubernetesServiceAccountsProducer.java @@ -34,6 +34,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesServiceAccountsProducer extends DefaultProducer { @@ -85,7 +86,7 @@ public class KubernetesServiceAccountsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); ServiceAccountList saList; if (ObjectHelper.isEmpty(namespace)) { @@ -98,7 +99,7 @@ public class KubernetesServiceAccountsProducer extends DefaultProducer { } protected void doListServiceAccountsByLabels(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_ACCOUNTS_LABELS, Map.class); ServiceAccountList saList; @@ -119,7 +120,7 @@ public class KubernetesServiceAccountsProducer extends DefaultProducer { protected void doGetServiceAccount(Exchange exchange) { String saName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_ACCOUNT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(saName)) { LOG.error("Get a specific Service Account require specify a Service Account name"); throw new IllegalArgumentException("Get a specific Service Account require specify a Service Account name"); @@ -144,7 +145,7 @@ public class KubernetesServiceAccountsProducer extends DefaultProducer { private void doCreateOrUpdateServiceAccount( Exchange exchange, String operationName, Function<Resource<ServiceAccount>, ServiceAccount> operation) { - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); ServiceAccount saToCreate = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_ACCOUNT, ServiceAccount.class); if (ObjectHelper.isEmpty(namespaceName)) { @@ -165,7 +166,7 @@ public class KubernetesServiceAccountsProducer extends DefaultProducer { protected void doDeleteServiceAccount(Exchange exchange) { String saName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_ACCOUNT_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(saName)) { LOG.error("Delete a specific Service Account require specify a Service Account name"); throw new IllegalArgumentException("Delete a specific Service Account require specify a Service Account name"); diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/services/KubernetesServicesProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/services/KubernetesServicesProducer.java index e78662b702a1..91bfd25f5b98 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/services/KubernetesServicesProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/kubernetes/services/KubernetesServicesProducer.java @@ -36,6 +36,7 @@ import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static org.apache.camel.component.kubernetes.KubernetesHelper.extractNamespace; import static org.apache.camel.component.kubernetes.KubernetesHelper.prepareOutboundMessage; public class KubernetesServicesProducer extends DefaultProducer { @@ -87,7 +88,7 @@ public class KubernetesServicesProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); ServiceList servicesList; if (ObjectHelper.isEmpty(namespace)) { @@ -99,7 +100,7 @@ public class KubernetesServicesProducer extends DefaultProducer { } protected void doListServiceByLabels(Exchange exchange) { - String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespace = extractNamespace(getEndpoint(), exchange); Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_LABELS, Map.class); ServiceList servicesList; @@ -119,7 +120,7 @@ public class KubernetesServicesProducer extends DefaultProducer { protected void doGetService(Exchange exchange) { String serviceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(serviceName)) { LOG.error("Get a specific service require specify a service name"); throw new IllegalArgumentException("Get a specific service require specify a service name"); @@ -144,7 +145,7 @@ public class KubernetesServicesProducer extends DefaultProducer { private void doCreateOrUpdateService( Exchange exchange, String operationName, Function<Resource<Service>, Service> operation) { String serviceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); ServiceSpec serviceSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_SPEC, ServiceSpec.class); if (ObjectHelper.isEmpty(serviceName)) { LOG.error("{} a specific service require specify a service name", operationName); @@ -173,7 +174,7 @@ public class KubernetesServicesProducer extends DefaultProducer { protected void doDeleteService(Exchange exchange) { String serviceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_SERVICE_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = extractNamespace(getEndpoint(), exchange); if (ObjectHelper.isEmpty(serviceName)) { LOG.error("Delete a specific service require specify a service name"); throw new IllegalArgumentException("Delete a specific service require specify a service name"); diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/producer/KubernetesCustomResourcesProducerCrudTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/producer/KubernetesCustomResourcesProducerCrudTest.java new file mode 100644 index 000000000000..f14aea96397a --- /dev/null +++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/producer/KubernetesCustomResourcesProducerCrudTest.java @@ -0,0 +1,186 @@ +/* + * 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. + */ +package org.apache.camel.component.kubernetes.producer; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.GenericKubernetesResource; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.kubernetes.client.utils.Serialization; +import org.apache.camel.BindToRegistry; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.kubernetes.KubernetesConstants; +import org.apache.camel.component.kubernetes.KubernetesTestSupport; +import org.apache.camel.util.IOHelper; +import org.apache.camel.util.json.JsonObject; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * The custom resources producer against the mock server in CRUD mode, with the custom resource definition and the + * namespace in the endpoint options only. + */ +@EnableKubernetesMockClient(crud = true) +public class KubernetesCustomResourcesProducerCrudTest extends KubernetesTestSupport { + + private static final String CRD_OPTIONS = "&namespace=testnamespace&crdName=githubsources.sources.knative.dev" + + "&crdGroup=sources.knative.dev&crdScope=Namespaced&crdVersion=v1alpha1" + + "&crdPlural=githubsources"; + + private static String githubSourceString; + + NamespacedKubernetesClient client; + + @BeforeAll + public static void readResource() throws IOException { + try (InputStream stream = KubernetesCustomResourcesProducerCrudTest.class.getResourceAsStream("sample-cr.json")) { + githubSourceString = IOHelper.loadText(stream); + } + } + + @BindToRegistry("kubernetesClient") + public KubernetesClient getClient() { + return client; + } + + @Test + void crdOptionsOnly() { + Exchange created = template.request("direct:create", + exchange -> exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE, githubSourceString)); + assertFalse(created.isFailed(), () -> "create failed: " + created.getException()); + assertEquals("samplecr", created.getMessage().getBody(GenericKubernetesResource.class).getMetadata().getName()); + + Exchange listed = template.request("direct:list", exchange -> { + }); + assertFalse(listed.isFailed(), () -> "list failed: " + listed.getException()); + assertEquals(1, listed.getMessage().getBody(List.class).size()); + + Exchange got = template.request("direct:get", + exchange -> exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE_NAME, "samplecr")); + assertFalse(got.isFailed(), () -> "get failed: " + got.getException()); + JsonObject json = got.getMessage().getBody(JsonObject.class); + assertEquals("https://api.github.com/", json.get("githubAPIURL")); + + Exchange deleted = template.request("direct:delete", + exchange -> exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE_NAME, "samplecr")); + assertFalse(deleted.isFailed(), () -> "delete failed: " + deleted.getException()); + assertTrue(deleted.getMessage().getHeader(KubernetesConstants.KUBERNETES_DELETE_RESULT, Boolean.class)); + } + + @Test + void getMissingGivesEmptyJsonObject() { + Exchange got = template.request("direct:getWithHeaders", exchange -> { + setCrdHeaders(exchange); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE_NAME, "missing"); + }); + + assertFalse(got.isFailed(), () -> "get failed: " + got.getException()); + assertTrue(got.getMessage().getBody(JsonObject.class).isEmpty()); + } + + @Test + void createExistingFails() { + GenericKubernetesResource resource = Serialization.unmarshal(githubSourceString, GenericKubernetesResource.class); + resource.getMetadata().setName("existing"); + String instance = Serialization.asJson(resource); + + Exchange first = template.request("direct:createWithHeaders", exchange -> { + setCrdHeaders(exchange); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE, instance); + }); + assertFalse(first.isFailed(), () -> "create failed: " + first.getException()); + + Exchange second = template.request("direct:createWithHeaders", exchange -> { + setCrdHeaders(exchange); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE, instance); + }); + assertTrue(second.isFailed()); + assertEquals(409, assertInstanceOf(KubernetesClientException.class, second.getException()).getCode()); + } + + @Test + void listByLabelsWithoutLabelsFails() { + Exchange listed = template.request("direct:listByLabelsWithHeaders", this::setCrdHeaders); + + assertTrue(listed.isFailed()); + assertInstanceOf(IllegalArgumentException.class, listed.getException()); + } + + @Test + void listByLabels() { + GenericKubernetesResource resource = Serialization.unmarshal(githubSourceString, GenericKubernetesResource.class); + resource.getMetadata().setName("labelled"); + resource.getMetadata().setLabels(Map.of("app", "camel")); + String instance = Serialization.asJson(resource); + Exchange created = template.request("direct:create", + exchange -> exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_INSTANCE, instance)); + assertFalse(created.isFailed(), () -> "create failed: " + created.getException()); + + Exchange listed = template.request("direct:listByLabels", + exchange -> exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_LABELS, Map.of("app", "camel"))); + + assertFalse(listed.isFailed(), () -> "list failed: " + listed.getException()); + assertEquals(1, listed.getMessage().getBody(List.class).size()); + } + + private void setCrdHeaders(Exchange exchange) { + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "testnamespace"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_NAME, "githubsources.sources.knative.dev"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_GROUP, "sources.knative.dev"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_SCOPE, "Namespaced"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_VERSION, "v1alpha1"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CRD_PLURAL, "githubsources"); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:create").to("kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient" + + "&operation=createCustomResource" + CRD_OPTIONS); + from("direct:list").to("kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient" + + "&operation=listCustomResources" + CRD_OPTIONS); + from("direct:listByLabels").to("kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient" + + "&operation=listCustomResourcesByLabels" + CRD_OPTIONS); + from("direct:get").to("kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient" + + "&operation=getCustomResource" + CRD_OPTIONS); + from("direct:createWithHeaders").to( + "kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient&operation=createCustomResource"); + from("direct:getWithHeaders").to( + "kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient&operation=getCustomResource"); + from("direct:listByLabelsWithHeaders").to( + "kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient" + + "&operation=listCustomResourcesByLabels"); + from("direct:delete").to("kubernetes-custom-resources:///?kubernetesClient=#kubernetesClient" + + "&operation=deleteCustomResource" + CRD_OPTIONS); + } + }; + } +} diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/producer/KubernetesProducersCrudTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/producer/KubernetesProducersCrudTest.java new file mode 100644 index 000000000000..44aed28cd18c --- /dev/null +++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/kubernetes/producer/KubernetesProducersCrudTest.java @@ -0,0 +1,204 @@ +/* + * 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. + */ +package org.apache.camel.component.kubernetes.producer; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ReplicationControllerBuilder; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretBuilder; +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.api.model.ServiceBuilder; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import org.apache.camel.BindToRegistry; +import org.apache.camel.Exchange; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.kubernetes.KubernetesConstants; +import org.apache.camel.component.kubernetes.KubernetesTestSupport; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Producers of the kubernetes components against the mock server in CRUD mode. + */ +@EnableKubernetesMockClient(crud = true) +public class KubernetesProducersCrudTest extends KubernetesTestSupport { + + NamespacedKubernetesClient client; + + @BindToRegistry("kubernetesClient") + public KubernetesClient getClient() { + return client; + } + + @Test + void scaleDeploymentWithoutStatus() { + client.apps().deployments().inNamespace("test").resource(new DeploymentBuilder().withNewMetadata().withName("de1") + .endMetadata().withNewSpec().withReplicas(1).endSpec().build()).create(); + + Exchange ex = template.request("direct:scaleDeployment", exchange -> { + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "test"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, "de1"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_REPLICAS, 3); + }); + + assertFalse(ex.isFailed(), () -> "scale failed: " + ex.getException()); + assertEquals(3, ex.getMessage().getBody(Integer.class)); + assertEquals(3, client.apps().deployments().inNamespace("test").withName("de1").get().getSpec().getReplicas()); + } + + @Test + void scaleReplicationControllerWithoutStatus() { + client.replicationControllers().inNamespace("test").resource(new ReplicationControllerBuilder().withNewMetadata() + .withName("rc1").endMetadata().withNewSpec().withReplicas(1).endSpec().build()).create(); + + Exchange ex = template.request("direct:scaleReplicationController", exchange -> { + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "test"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_NAME, "rc1"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_REPLICATION_CONTROLLER_REPLICAS, 2); + }); + + assertFalse(ex.isFailed(), () -> "scale failed: " + ex.getException()); + assertEquals(2, ex.getMessage().getBody(Integer.class)); + } + + @Test + void namespaceOptionWhenNoHeader() { + client.services().inNamespace("test").resource(new ServiceBuilder().withNewMetadata().withName("svc1") + .endMetadata().build()).create(); + client.services().inNamespace("other").resource(new ServiceBuilder().withNewMetadata().withName("svc2") + .endMetadata().build()).create(); + + Exchange got = template.request("direct:getService", + exchange -> exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_SERVICE_NAME, "svc1")); + assertFalse(got.isFailed(), () -> "get failed: " + got.getException()); + assertEquals("svc1", got.getMessage().getBody(Service.class).getMetadata().getName()); + + Exchange listed = template.request("direct:listServices", exchange -> { + }); + assertFalse(listed.isFailed(), () -> "list failed: " + listed.getException()); + List<?> services = listed.getMessage().getBody(List.class); + assertEquals(1, services.size()); + assertEquals("svc1", ((Service) services.get(0)).getMetadata().getName()); + } + + @Test + void namespaceHeaderWinsOverOption() { + client.configMaps().inNamespace("other").resource(new ConfigMapBuilder().withNewMetadata().withName("cm1") + .endMetadata().addToData("key", "value").build()).create(); + + Exchange got = template.request("direct:getConfigMap", exchange -> { + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_CONFIGMAP_NAME, "cm1"); + }); + + assertFalse(got.isFailed(), () -> "get failed: " + got.getException()); + assertNotNull(got.getMessage().getBody()); + } + + @Test + void listPersistentVolumesByLabelsWithoutLabelsFails() { + Exchange ex = template.request("direct:listPersistentVolumesByLabels", exchange -> { + }); + + assertTrue(ex.isFailed()); + assertInstanceOf(IllegalArgumentException.class, ex.getException()); + } + + @Test + void listNodesByLabelsWithoutLabelsFails() { + Exchange ex = template.request("direct:listNodesByLabels", exchange -> { + }); + + assertTrue(ex.isFailed()); + assertInstanceOf(IllegalArgumentException.class, ex.getException()); + } + + @Test + void createSecretWithAnnotationsKeepsTheSecretOfTheHeader() { + Secret secret = new SecretBuilder().withNewMetadata().withName("secret1").endMetadata() + .addToStringData("password", "changeit").build(); + Map<String, String> annotations = new HashMap<>(); + annotations.put("app", "camel"); + + Exchange ex = template.request("direct:createSecret", exchange -> { + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "test"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_SECRET, secret); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_SECRETS_ANNOTATIONS, annotations); + }); + + assertFalse(ex.isFailed(), () -> "create failed: " + ex.getException()); + assertEquals(annotations, ex.getMessage().getBody(Secret.class).getMetadata().getAnnotations()); + assertTrue(secret.getMetadata().getAnnotations() == null || secret.getMetadata().getAnnotations().isEmpty()); + } + + @Test + void createSecretWithAnnotationsWithoutMetadata() { + Secret secret = new SecretBuilder().addToStringData("password", "changeit").build(); + Map<String, String> annotations = new HashMap<>(); + annotations.put("app", "camel"); + + Exchange ex = template.request("direct:createSecret", exchange -> { + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "test"); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_SECRET, secret); + exchange.getIn().setHeader(KubernetesConstants.KUBERNETES_SECRETS_ANNOTATIONS, annotations); + }); + + // the mock server takes a Secret without a name: what matters is that it is sent, not a NullPointerException + assertFalse(ex.getException() instanceof NullPointerException, () -> "create failed: " + ex.getException()); + assertNull(secret.getMetadata()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:scaleDeployment").to( + "kubernetes-deployments:///?kubernetesClient=#kubernetesClient&operation=scaleDeployment"); + from("direct:scaleReplicationController").to( + "kubernetes-replication-controllers:///?kubernetesClient=#kubernetesClient" + + "&operation=scaleReplicationController"); + from("direct:getService").to( + "kubernetes-services:///?kubernetesClient=#kubernetesClient&operation=getService&namespace=test"); + from("direct:listServices").to( + "kubernetes-services:///?kubernetesClient=#kubernetesClient&operation=listServices&namespace=test"); + from("direct:getConfigMap").to( + "kubernetes-config-maps:///?kubernetesClient=#kubernetesClient&operation=getConfigMap&namespace=test"); + from("direct:listPersistentVolumesByLabels").to( + "kubernetes-persistent-volumes:///?kubernetesClient=#kubernetesClient" + + "&operation=listPersistentVolumesByLabels"); + from("direct:listNodesByLabels").to( + "kubernetes-nodes:///?kubernetesClient=#kubernetesClient&operation=listNodesByLabels"); + from("direct:createSecret").to( + "kubernetes-secrets:///?kubernetesClient=#kubernetesClient&operation=createSecret"); + } + }; + } +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 7c82ebd18930..5e00adac7733 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -660,6 +660,24 @@ which embeds its own relocated copy of Vert.x. The `vertx` option of `camel-aran See the https://github.com/fabric8io/kubernetes-client/blob/main/doc/MIGRATION-v8.md[Kubernetes client 8 migration guide] for the other changes. +=== camel-kubernetes - the namespace option, custom resources and scale + +* The producers of the namespaced `kubernetes-*` components (config-maps, cronjob, deployments, events, hpa, job, + persistent-volumes-claims, pods, replication-controllers, resources-quota, secrets, service-accounts and services) + use the `namespace` endpoint option when the `CamelKubernetesNamespaceName` header is not set; the header still + wins. Before, these producers ignored the option: the get, create, update and delete operations failed without + the header, and the list operations listed all namespaces. A route that sets the `namespace` option on a producer + endpoint and lists without the header now lists that namespace only. +* The `kubernetes-custom-resources` producer uses the `crdName`, `crdGroup`, `crdScope`, `crdVersion` and + `crdPlural` endpoint options. Before, it checked them but then built the custom resource definition from the + headers only. +* `createCustomResource` and `updateCustomResource` fail the exchange with the `KubernetesClientException` of a + 409 Conflict (the instance already exists, or the update is based on an old resource version). Before, the + conflict was logged at INFO and the body was an empty `GenericKubernetesResource`. +* `scaleDeployment` and `scaleReplicationController` return the replicas of the spec of the scaled resource, the + number that was asked for. Before, they returned the replicas of its status, which lags behind until the pods are + started or stopped, and failed with a `NullPointerException` when the resource had no status yet. + === camel-langchain4j-embeddingstore - auto-embedding a TextSegment, a List, a Map or a POJO body, and the REMOVE id With an `embeddingModel` and no `CamelLangChain4jEmbeddingsEmbedding` header, ADD and SEARCH now embed a `TextSegment`
