This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26241-openshift-namespace in repository https://gitbox.apache.org/repos/asf/camel.git
commit 965c4814330923177a15567c88e48683c51264dd Author: Claus Ibsen <[email protected]> AuthorDate: Tue Nov 3 15:29:28 2026 +0100 CAMEL-26241: camel-kubernetes - openshift producers use the namespace header and option in every operation The openshift-deploymentconfigs list, listByLabels and get operations ignored the CamelKubernetesNamespaceName header, and listBuilds and listBuildConfigs always listed all namespaces. All three producers now take the namespace from the header, or else from the namespace option (used by the consumer only before). The ByLabels operations fail with an IllegalArgumentException when the labels header is missing, instead of a NullPointerException. scaleDeploymentConfig returns the requested replicas instead of the replicas of the status, which may lag behind or be missing (NullPointerException). Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../components/openshift-build-configs.json | 2 +- .../camel/catalog/components/openshift-builds.json | 2 +- .../components/openshift-deploymentconfigs.json | 4 +- .../docs/openshift-build-configs-component.adoc | 6 +- .../catalog/docs/openshift-builds-component.adoc | 4 + .../openshift-deploymentconfigs-component.adoc | 7 +- .../build_configs/openshift-build-configs.json | 2 +- .../openshift/builds/openshift-builds.json | 2 +- .../openshift-deploymentconfigs.json | 4 +- .../docs/openshift-build-configs-component.adoc | 6 +- .../src/main/docs/openshift-builds-component.adoc | 4 + .../openshift-deploymentconfigs-component.adoc | 7 +- .../OpenshiftBuildConfigsEndpoint.java | 6 +- .../OpenshiftBuildConfigsProducer.java | 27 +- .../openshift/builds/OpenshiftBuildsEndpoint.java | 6 +- .../openshift/builds/OpenshiftBuildsProducer.java | 25 +- .../OpenshiftDeploymentConfigsEndpoint.java | 8 +- .../OpenshiftDeploymentConfigsProducer.java | 49 ++-- .../OpenshiftDeploymentConfigsProducerTest.java | 3 +- .../producer/OpenshiftProducersNamespaceTest.java | 278 +++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 19 ++ 21 files changed, 426 insertions(+), 45 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-build-configs.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-build-configs.json index 1e122503e63b..76c8fcbc503c 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-build-configs.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-build-configs.json @@ -35,7 +35,7 @@ "CamelKubernetesBuildConfigName": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Openshift Config Build name", "constantName": "org.apache.camel.component.kubernetes.KubernetesConstants#KUBERNETES_BUILD_CONFIG_NAME" } }, "bodyTypes": { - "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 CamelKubernetesBuildConfigName and the labels in CamelKubernetesBuildConfigsLabels. listBuildConfigs does not read the namespace header: it lists the build configs of all namespaces" }, + "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 (else the namespace option), the name in CamelKubernetesBuildConfigName and the labels in CamelKubernetesBuildConfigsLabels. Without a namespace the list operations list the build configs of all namespaces" }, "producerOut": { "carriers": [ "list", "pojo", "null" ], "byOption": [ { "option": "operation", "value": "listBuildConfigs", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.BuildConfig>" }, { "option": "operation", "value": "listBuildConfigsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.BuildConfig>" }, { "option": "operation", "value": "getBuildConfig", "carriers": [ "pojo", "null" ], "javaType": "io.f [...] }, "properties": { diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-builds.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-builds.json index 04b0baf9f398..faee0219feb4 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-builds.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-builds.json @@ -35,7 +35,7 @@ "CamelKubernetesBuildName": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The OpenShift build name", "constantName": "org.apache.camel.component.kubernetes.KubernetesConstants#KUBERNETES_BUILD_NAME" } }, "bodyTypes": { - "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 CamelKubernetesBuildName and the labels in CamelKubernetesBuildsLabels. listBuilds does not read the namespace header: it lists the builds of all namespaces" }, + "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 (else the namespace option), the name in CamelKubernetesBuildName and the labels in CamelKubernetesBuildsLabels. Without a namespace the list operations list the builds of all namespaces" }, "producerOut": { "carriers": [ "list", "pojo", "null" ], "byOption": [ { "option": "operation", "value": "listBuilds", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.Build>" }, { "option": "operation", "value": "listBuildsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.Build>" }, { "option": "operation", "value": "getBuild", "carriers": [ "pojo", "null" ], "javaType": "io.fabric8.openshift.api.model.Bui [...] }, "properties": { diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-deploymentconfigs.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-deploymentconfigs.json index 3867981619cf..2e92ab392e69 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-deploymentconfigs.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/openshift-deploymentconfigs.json @@ -42,8 +42,8 @@ }, "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "io.fabric8.openshift.api.model.DeploymentConfig", "description": "The DeploymentConfig 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 CamelKubernetesDeploymentName, the labels in CamelKubernetesDeploymentsLabels, the DeploymentConfigSpec in CamelKubernetesDeploymentConfigSpec and the replicas in CamelKubernetesDeploymentReplicas. The list and get operations do not read the namespace [...] - "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeploymentConfigs", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "listDeploymentConfigsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "getDeploymentConfig", "carriers": [ " [...] + "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 (else the namespace option), the name in CamelKubernetesDeploymentName, the labels in CamelKubernetesDeploymentsLabels, the DeploymentConfigSpec in CamelKubernetesDeploymentConfigSpec and the replicas in CamelKubernetesDeploymentReplicas. Without a namespace the li [...] + "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeploymentConfigs", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "listDeploymentConfigsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "getDeploymentConfig", "carriers": [ " [...] }, "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/openshift-build-configs-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-build-configs-component.adoc index acaf39923331..9ed97eb8db8b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-build-configs-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-build-configs-component.adoc @@ -33,6 +33,10 @@ include::partial$component-endpoint-headers.adoc[] - `listBuildConfigsByLabels` - `getBuildConfig` +The operations use the namespace of the `CamelKubernetesNamespaceName` header, or else of the `namespace` option. +Without a namespace, `listBuildConfigs` and `listBuildConfigsByLabels` list the build configs of all namespaces; +`getBuildConfig` requires one. `listBuildConfigsByLabels` requires the `CamelKubernetesBuildConfigsLabels` header. + == Examples === Openshift Build Configs Producer Examples @@ -81,7 +85,7 @@ YAML:: This operation returns a list of builds from your Openshift cluster -- `listBuildsByLabels`: this operation lists the build configs by labels on an Openshift cluster +- `listBuildConfigsByLabels`: this operation lists the build configs by labels on an Openshift cluster ._Java-only: sets a Map header for label selection_ [source,java] diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-builds-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-builds-component.adoc index 32103e013102..af8890ba729b 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-builds-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-builds-component.adoc @@ -33,6 +33,10 @@ include::partial$component-endpoint-headers.adoc[] - `listBuildsByLabels` - `getBuild` +The operations use the namespace of the `CamelKubernetesNamespaceName` header, or else of the `namespace` option. +Without a namespace, `listBuilds` and `listBuildsByLabels` list the builds of all namespaces; `getBuild` requires one. +`listBuildsByLabels` requires the `CamelKubernetesBuildsLabels` header. + == Examples === Openshift Builds Producer Examples diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-deploymentconfigs-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-deploymentconfigs-component.adoc index 1e7848766b68..ac9606bb2032 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-deploymentconfigs-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/openshift-deploymentconfigs-component.adoc @@ -28,13 +28,18 @@ include::partial$component-endpoint-headers.adoc[] === Supported producer operation - `listDeploymentConfigs` -- `listDeploymentsConfigsByLabels` +- `listDeploymentConfigsByLabels` - `getDeploymentConfig` - `createDeploymentConfig` - `updateDeploymentConfig` - `deleteDeploymentConfig` - `scaleDeploymentConfig` +The operations use the namespace of the `CamelKubernetesNamespaceName` header, or else of the `namespace` option. +Without a namespace, `listDeploymentConfigs`, `listDeploymentConfigsByLabels` and `getDeploymentConfig` use the +namespace of the Kubernetes client; the other operations require one. `listDeploymentConfigsByLabels` requires the +`CamelKubernetesDeploymentsLabels` header. `scaleDeploymentConfig` returns the requested number of replicas. + == Examples === Openshift Deployment Configs Producer Examples diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/build_configs/openshift-build-configs.json b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/build_configs/openshift-build-configs.json index 1e122503e63b..76c8fcbc503c 100644 --- a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/build_configs/openshift-build-configs.json +++ b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/build_configs/openshift-build-configs.json @@ -35,7 +35,7 @@ "CamelKubernetesBuildConfigName": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The Openshift Config Build name", "constantName": "org.apache.camel.component.kubernetes.KubernetesConstants#KUBERNETES_BUILD_CONFIG_NAME" } }, "bodyTypes": { - "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 CamelKubernetesBuildConfigName and the labels in CamelKubernetesBuildConfigsLabels. listBuildConfigs does not read the namespace header: it lists the build configs of all namespaces" }, + "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 (else the namespace option), the name in CamelKubernetesBuildConfigName and the labels in CamelKubernetesBuildConfigsLabels. Without a namespace the list operations list the build configs of all namespaces" }, "producerOut": { "carriers": [ "list", "pojo", "null" ], "byOption": [ { "option": "operation", "value": "listBuildConfigs", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.BuildConfig>" }, { "option": "operation", "value": "listBuildConfigsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.BuildConfig>" }, { "option": "operation", "value": "getBuildConfig", "carriers": [ "pojo", "null" ], "javaType": "io.f [...] }, "properties": { diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/builds/openshift-builds.json b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/builds/openshift-builds.json index 04b0baf9f398..faee0219feb4 100644 --- a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/builds/openshift-builds.json +++ b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/builds/openshift-builds.json @@ -35,7 +35,7 @@ "CamelKubernetesBuildName": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "producer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The OpenShift build name", "constantName": "org.apache.camel.component.kubernetes.KubernetesConstants#KUBERNETES_BUILD_NAME" } }, "bodyTypes": { - "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 CamelKubernetesBuildName and the labels in CamelKubernetesBuildsLabels. listBuilds does not read the namespace header: it lists the builds of all namespaces" }, + "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 (else the namespace option), the name in CamelKubernetesBuildName and the labels in CamelKubernetesBuildsLabels. Without a namespace the list operations list the builds of all namespaces" }, "producerOut": { "carriers": [ "list", "pojo", "null" ], "byOption": [ { "option": "operation", "value": "listBuilds", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.Build>" }, { "option": "operation", "value": "listBuildsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.Build>" }, { "option": "operation", "value": "getBuild", "carriers": [ "pojo", "null" ], "javaType": "io.fabric8.openshift.api.model.Bui [...] }, "properties": { diff --git a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/deploymentconfigs/openshift-deploymentconfigs.json b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/deploymentconfigs/openshift-deploymentconfigs.json index 3867981619cf..2e92ab392e69 100644 --- a/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/deploymentconfigs/openshift-deploymentconfigs.json +++ b/components/camel-kubernetes/src/generated/resources/META-INF/org/apache/camel/component/openshift/deploymentconfigs/openshift-deploymentconfigs.json @@ -42,8 +42,8 @@ }, "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "io.fabric8.openshift.api.model.DeploymentConfig", "description": "The DeploymentConfig 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 CamelKubernetesDeploymentName, the labels in CamelKubernetesDeploymentsLabels, the DeploymentConfigSpec in CamelKubernetesDeploymentConfigSpec and the replicas in CamelKubernetesDeploymentReplicas. The list and get operations do not read the namespace [...] - "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeploymentConfigs", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "listDeploymentConfigsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "getDeploymentConfig", "carriers": [ " [...] + "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 (else the namespace option), the name in CamelKubernetesDeploymentName, the labels in CamelKubernetesDeploymentsLabels, the DeploymentConfigSpec in CamelKubernetesDeploymentConfigSpec and the replicas in CamelKubernetesDeploymentReplicas. Without a namespace the li [...] + "producerOut": { "carriers": [ "list", "pojo", "null", "value" ], "byOption": [ { "option": "operation", "value": "listDeploymentConfigs", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "listDeploymentConfigsByLabels", "carriers": [ "list" ], "javaType": "java.util.List<io.fabric8.openshift.api.model.DeploymentConfig>" }, { "option": "operation", "value": "getDeploymentConfig", "carriers": [ " [...] }, "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/openshift-build-configs-component.adoc b/components/camel-kubernetes/src/main/docs/openshift-build-configs-component.adoc index acaf39923331..9ed97eb8db8b 100644 --- a/components/camel-kubernetes/src/main/docs/openshift-build-configs-component.adoc +++ b/components/camel-kubernetes/src/main/docs/openshift-build-configs-component.adoc @@ -33,6 +33,10 @@ include::partial$component-endpoint-headers.adoc[] - `listBuildConfigsByLabels` - `getBuildConfig` +The operations use the namespace of the `CamelKubernetesNamespaceName` header, or else of the `namespace` option. +Without a namespace, `listBuildConfigs` and `listBuildConfigsByLabels` list the build configs of all namespaces; +`getBuildConfig` requires one. `listBuildConfigsByLabels` requires the `CamelKubernetesBuildConfigsLabels` header. + == Examples === Openshift Build Configs Producer Examples @@ -81,7 +85,7 @@ YAML:: This operation returns a list of builds from your Openshift cluster -- `listBuildsByLabels`: this operation lists the build configs by labels on an Openshift cluster +- `listBuildConfigsByLabels`: this operation lists the build configs by labels on an Openshift cluster ._Java-only: sets a Map header for label selection_ [source,java] diff --git a/components/camel-kubernetes/src/main/docs/openshift-builds-component.adoc b/components/camel-kubernetes/src/main/docs/openshift-builds-component.adoc index 32103e013102..af8890ba729b 100644 --- a/components/camel-kubernetes/src/main/docs/openshift-builds-component.adoc +++ b/components/camel-kubernetes/src/main/docs/openshift-builds-component.adoc @@ -33,6 +33,10 @@ include::partial$component-endpoint-headers.adoc[] - `listBuildsByLabels` - `getBuild` +The operations use the namespace of the `CamelKubernetesNamespaceName` header, or else of the `namespace` option. +Without a namespace, `listBuilds` and `listBuildsByLabels` list the builds of all namespaces; `getBuild` requires one. +`listBuildsByLabels` requires the `CamelKubernetesBuildsLabels` header. + == Examples === Openshift Builds Producer Examples diff --git a/components/camel-kubernetes/src/main/docs/openshift-deploymentconfigs-component.adoc b/components/camel-kubernetes/src/main/docs/openshift-deploymentconfigs-component.adoc index 1e7848766b68..ac9606bb2032 100644 --- a/components/camel-kubernetes/src/main/docs/openshift-deploymentconfigs-component.adoc +++ b/components/camel-kubernetes/src/main/docs/openshift-deploymentconfigs-component.adoc @@ -28,13 +28,18 @@ include::partial$component-endpoint-headers.adoc[] === Supported producer operation - `listDeploymentConfigs` -- `listDeploymentsConfigsByLabels` +- `listDeploymentConfigsByLabels` - `getDeploymentConfig` - `createDeploymentConfig` - `updateDeploymentConfig` - `deleteDeploymentConfig` - `scaleDeploymentConfig` +The operations use the namespace of the `CamelKubernetesNamespaceName` header, or else of the `namespace` option. +Without a namespace, `listDeploymentConfigs`, `listDeploymentConfigsByLabels` and `getDeploymentConfig` use the +namespace of the Kubernetes client; the other operations require one. `listDeploymentConfigsByLabels` requires the +`CamelKubernetesDeploymentsLabels` header. `scaleDeploymentConfig` returns the requested number of replicas. + == Examples === Openshift Deployment Configs Producer Examples diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsEndpoint.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsEndpoint.java index 7be032e4843f..3335981084d3 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsEndpoint.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsEndpoint.java @@ -40,10 +40,10 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_B @BodyTypes(producerIn = @BodyType(value = Carrier.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" + + " namespace in CamelKubernetesNamespaceName (else the namespace option), the name in" + " CamelKubernetesBuildConfigName and the labels in" - + " CamelKubernetesBuildConfigsLabels. listBuildConfigs does not read the" - + " namespace header: it lists the build configs of all namespaces"), + + " CamelKubernetesBuildConfigsLabels. Without a namespace the list operations" + + " list the build configs of all namespaces"), producerOut = @BodyType(value = { Carrier.LIST, Carrier.POJO, Carrier.NULL }, byOption = { @BodyType.When(option = "operation", value = "listBuildConfigs", diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsProducer.java index 6057993c6454..d9c06eb3c096 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/build_configs/OpenshiftBuildConfigsProducer.java @@ -70,14 +70,25 @@ public class OpenshiftBuildConfigsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - BuildConfigList buildConfigsList - = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).buildConfigs().inAnyNamespace().list(); + String namespaceName = getNamespace(exchange); + BuildConfigList buildConfigsList; + if (!ObjectHelper.isEmpty(namespaceName)) { + buildConfigsList = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).buildConfigs() + .inNamespace(namespaceName).list(); + } else { + buildConfigsList = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).buildConfigs() + .inAnyNamespace().list(); + } exchange.getMessage().setBody(buildConfigsList.getItems()); } protected void doListBuildConfigsByLabels(Exchange exchange) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_BUILD_CONFIGS_LABELS, Map.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + if (ObjectHelper.isEmpty(labels)) { + LOG.error("Listing Build Configs by labels requires specifying labels"); + throw new IllegalArgumentException("Listing Build Configs by labels requires specifying labels"); + } + String namespaceName = getNamespace(exchange); BuildConfigList buildConfigsList; if (!ObjectHelper.isEmpty(namespaceName)) { buildConfigsList = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).buildConfigs() @@ -91,7 +102,7 @@ public class OpenshiftBuildConfigsProducer extends DefaultProducer { protected void doGetBuildConfig(Exchange exchange) { String buildConfigName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_BUILD_CONFIG_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = getNamespace(exchange); if (ObjectHelper.isEmpty(buildConfigName)) { LOG.error("Get a specific Build Config require specify a Build Config name"); throw new IllegalArgumentException("Get a specific Build Config require specify a Build Config name"); @@ -106,4 +117,12 @@ public class OpenshiftBuildConfigsProducer extends DefaultProducer { prepareOutboundMessage(exchange, buildConfig); } + + private String getNamespace(Exchange exchange) { + String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + if (ObjectHelper.isEmpty(namespace)) { + namespace = getEndpoint().getKubernetesConfiguration().getNamespace(); + } + return namespace; + } } diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsEndpoint.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsEndpoint.java index 6ca7f6290ebf..febddab5c736 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsEndpoint.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsEndpoint.java @@ -40,9 +40,9 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_B @BodyTypes(producerIn = @BodyType(value = Carrier.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" - + " CamelKubernetesBuildName and the labels in CamelKubernetesBuildsLabels. listBuilds does not" - + " read the namespace header: it lists the builds of all namespaces"), + + " namespace in CamelKubernetesNamespaceName (else the namespace option), the name in" + + " CamelKubernetesBuildName and the labels in CamelKubernetesBuildsLabels. Without a" + + " namespace the list operations list the builds of all namespaces"), producerOut = @BodyType(value = { Carrier.LIST, Carrier.POJO, Carrier.NULL }, byOption = { @BodyType.When(option = "operation", value = "listBuilds", carriers = Carrier.LIST, diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsProducer.java index 5a8d39524791..126a92db711f 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/builds/OpenshiftBuildsProducer.java @@ -70,13 +70,24 @@ public class OpenshiftBuildsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - BuildList buildList = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).builds().inAnyNamespace().list(); + String namespaceName = getNamespace(exchange); + BuildList buildList; + if (!ObjectHelper.isEmpty(namespaceName)) { + buildList = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).builds().inNamespace(namespaceName) + .list(); + } else { + buildList = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).builds().inAnyNamespace().list(); + } exchange.getMessage().setBody(buildList.getItems()); } protected void doListBuildByLabels(Exchange exchange) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_BUILDS_LABELS, Map.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + if (ObjectHelper.isEmpty(labels)) { + LOG.error("Listing Builds by labels requires specifying labels"); + throw new IllegalArgumentException("Listing Builds by labels requires specifying labels"); + } + String namespaceName = getNamespace(exchange); BuildList buildList; if (!ObjectHelper.isEmpty(namespaceName)) { buildList = getEndpoint().getKubernetesClient() @@ -91,7 +102,7 @@ public class OpenshiftBuildsProducer extends DefaultProducer { protected void doGetBuild(Exchange exchange) { String buildName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_BUILD_NAME, String.class); - String namespaceName = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + String namespaceName = getNamespace(exchange); if (ObjectHelper.isEmpty(buildName)) { LOG.error("Get a specific Build require specify a Build name"); throw new IllegalArgumentException("Get a specific Build require specify a Build name"); @@ -105,4 +116,12 @@ public class OpenshiftBuildsProducer extends DefaultProducer { prepareOutboundMessage(exchange, build); } + + private String getNamespace(Exchange exchange) { + String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + if (ObjectHelper.isEmpty(namespace)) { + namespace = getEndpoint().getKubernetesConfiguration().getNamespace(); + } + return namespace; + } } diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsEndpoint.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsEndpoint.java index 61dc7ce73a66..f7d117a2071d 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsEndpoint.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsEndpoint.java @@ -43,12 +43,12 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_D producerIn = @BodyType(value = Carrier.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" + + " namespace in CamelKubernetesNamespaceName (else the namespace option), the name in" + " CamelKubernetesDeploymentName, the labels in" + " CamelKubernetesDeploymentsLabels, the DeploymentConfigSpec in" + " CamelKubernetesDeploymentConfigSpec and the replicas in" - + " CamelKubernetesDeploymentReplicas. The list and get operations do not read the namespace" - + " header: they use the namespace of the client"), + + " CamelKubernetesDeploymentReplicas. Without a namespace the list and get operations" + + " use the namespace of the client"), producerOut = @BodyType(value = { Carrier.LIST, Carrier.POJO, Carrier.NULL, Carrier.VALUE }, byOption = { @BodyType.When(option = "operation", value = "listDeploymentConfigs", @@ -76,7 +76,7 @@ import static org.apache.camel.component.kubernetes.KubernetesConstants.SCHEME_D + " listDeploymentConfigsByLabels; the DeploymentConfig for" + " getDeploymentConfig (null when not found), createDeploymentConfig and" + " updateDeploymentConfig; a Boolean for deleteDeploymentConfig, false when" - + " not found; for scaleDeploymentConfig the replicas of its status, as an" + + " not found; for scaleDeploymentConfig the requested replicas, as an" + " Integer")) public class OpenshiftDeploymentConfigsEndpoint extends AbstractKubernetesEndpoint { diff --git a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsProducer.java b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsProducer.java index ef588d2aedbb..28e0016c3f30 100644 --- a/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsProducer.java +++ b/components/camel-kubernetes/src/main/java/org/apache/camel/component/openshift/deploymentconfigs/OpenshiftDeploymentConfigsProducer.java @@ -21,12 +21,15 @@ import java.util.Map; import java.util.function.Function; import io.fabric8.kubernetes.api.model.StatusDetails; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.openshift.api.model.DeploymentConfig; import io.fabric8.openshift.api.model.DeploymentConfigBuilder; import io.fabric8.openshift.api.model.DeploymentConfigList; import io.fabric8.openshift.api.model.DeploymentConfigSpec; import io.fabric8.openshift.client.OpenShiftClient; +import io.fabric8.openshift.client.dsl.DeployableScalableResource; import org.apache.camel.Exchange; import org.apache.camel.component.kubernetes.AbstractKubernetesEndpoint; import org.apache.camel.component.kubernetes.KubernetesConstants; @@ -92,17 +95,18 @@ public class OpenshiftDeploymentConfigsProducer extends DefaultProducer { } protected void doList(Exchange exchange) { - DeploymentConfigList deploymentConfigList - = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).deploymentConfigs().list(); + DeploymentConfigList deploymentConfigList = deploymentConfigs(getNamespace(exchange)).list(); prepareOutboundMessage(exchange, deploymentConfigList.getItems()); } protected void doListDeploymentConfigsByLabels(Exchange exchange) { Map<String, String> labels = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENTS_LABELS, Map.class); - DeploymentConfigList deploymentConfigList - = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).deploymentConfigs() - .withLabels(labels).list(); + if (ObjectHelper.isEmpty(labels)) { + LOG.error("Listing Deployment Configs by labels requires specifying labels"); + throw new IllegalArgumentException("Listing Deployment Configs by labels requires specifying labels"); + } + DeploymentConfigList deploymentConfigList = deploymentConfigs(getNamespace(exchange)).withLabels(labels).list(); prepareOutboundMessage(exchange, deploymentConfigList.getItems()); } @@ -113,15 +117,14 @@ public class OpenshiftDeploymentConfigsProducer extends DefaultProducer { LOG.error("Get a specific Deployment Config require specify a Deployment name"); throw new IllegalArgumentException("Get a specific Deployment Config require specify a Deployment Config name"); } - DeploymentConfig deploymentConfig = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).deploymentConfigs() - .withName(deploymentConfigName).get(); + DeploymentConfig deploymentConfig = deploymentConfigs(getNamespace(exchange)).withName(deploymentConfigName).get(); prepareOutboundMessage(exchange, deploymentConfig); } protected void doDeleteDeploymentConfig(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 = getNamespace(exchange); if (ObjectHelper.isEmpty(deploymentName)) { LOG.error("Delete a specific deployment config require specify a deployment name"); throw new IllegalArgumentException("Delete a specific deployment require specify a deployment config name"); @@ -150,7 +153,7 @@ public class OpenshiftDeploymentConfigsProducer extends DefaultProducer { private void doCreateOrUpdateDeployment( Exchange exchange, String operationName, Function<Resource<DeploymentConfig>, DeploymentConfig> 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 = getNamespace(exchange); DeploymentConfigSpec dcSpec = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_CONFIG_SPEC, DeploymentConfigSpec.class); if (ObjectHelper.isEmpty(deploymentName)) { @@ -182,7 +185,7 @@ public class OpenshiftDeploymentConfigsProducer extends DefaultProducer { protected void doScaleDeploymentConfig(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 = getNamespace(exchange); Integer replicasNumber = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_REPLICAS, Integer.class); if (ObjectHelper.isEmpty(deploymentName)) { LOG.error("Scale a specific deployment config require specify a deployment config name"); @@ -196,11 +199,27 @@ public class OpenshiftDeploymentConfigsProducer extends DefaultProducer { LOG.error("Scale a specific deployment config require specify a replicas number"); throw new IllegalArgumentException("Scale a specific deployment config require specify a replicas number"); } - DeploymentConfig deploymentConfigScaled - = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).deploymentConfigs() - .inNamespace(namespaceName) - .withName(deploymentName).scale(replicasNumber); + getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).deploymentConfigs() + .inNamespace(namespaceName) + .withName(deploymentName).scale(replicasNumber); + + // the status of the scaled config may not have the new count yet, or no status at all: return the requested count + prepareOutboundMessage(exchange, replicasNumber); + } + + private String getNamespace(Exchange exchange) { + String namespace = exchange.getIn().getHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, String.class); + if (ObjectHelper.isEmpty(namespace)) { + namespace = getEndpoint().getKubernetesConfiguration().getNamespace(); + } + return namespace; + } - prepareOutboundMessage(exchange, deploymentConfigScaled.getStatus().getReplicas()); + private NonNamespaceOperation<DeploymentConfig, DeploymentConfigList, DeployableScalableResource<DeploymentConfig>> deploymentConfigs( + String namespace) { + MixedOperation<DeploymentConfig, DeploymentConfigList, DeployableScalableResource<DeploymentConfig>> operation + = getEndpoint().getKubernetesClient().adapt(OpenShiftClient.class).deploymentConfigs(); + // without a namespace use the namespace of the client + return ObjectHelper.isEmpty(namespace) ? operation : operation.inNamespace(namespace); } } diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftDeploymentConfigsProducerTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftDeploymentConfigsProducerTest.java index 6aa1073995b0..8d3815bf3358 100644 --- a/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftDeploymentConfigsProducerTest.java +++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftDeploymentConfigsProducerTest.java @@ -177,7 +177,8 @@ public class OpenshiftDeploymentConfigsProducerTest extends KubernetesTestSuppor int replicas = ex.getMessage().getBody(Integer.class); - assertEquals(5, replicas); + // the requested replicas, not the (lagging) replicas of the status + assertEquals(1, replicas); } @Override diff --git a/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftProducersNamespaceTest.java b/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftProducersNamespaceTest.java new file mode 100644 index 000000000000..b73e8b5a777e --- /dev/null +++ b/components/camel-kubernetes/src/test/java/org/apache/camel/component/openshift/producer/OpenshiftProducersNamespaceTest.java @@ -0,0 +1,278 @@ +/* + * 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.openshift.producer; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.NamespacedKubernetesClient; +import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; +import io.fabric8.openshift.api.model.BuildBuilder; +import io.fabric8.openshift.api.model.BuildConfigBuilder; +import io.fabric8.openshift.api.model.DeploymentConfig; +import io.fabric8.openshift.api.model.DeploymentConfigBuilder; +import io.fabric8.openshift.api.model.DeploymentConfigSpecBuilder; +import io.fabric8.openshift.client.OpenShiftClient; +import org.apache.camel.BindToRegistry; +import org.apache.camel.CamelExecutionException; +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.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +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.assertThrows; + +/** + * The namespace of the openshift producers: the CamelKubernetesNamespaceName header, else the namespace option, else + * the namespace of the client (deployment configs) or all namespaces (builds and build configs). + */ +@EnableKubernetesMockClient(crud = true) +class OpenshiftProducersNamespaceTest extends KubernetesTestSupport { + + private static final Map<String, String> LABELS = Map.of("app", "demo"); + + NamespacedKubernetesClient client; + + @BindToRegistry("kubernetesClient") + public NamespacedKubernetesClient getClient() { + return client; + } + + @BeforeEach + void createResources() { + OpenShiftClient openShift = client.adapt(OpenShiftClient.class); + // the mock client uses the namespace "test" + for (String namespace : List.of("test", "other")) { + openShift.deploymentConfigs().inNamespace(namespace).resource(new DeploymentConfigBuilder().withNewMetadata() + .withName("dc-" + namespace).withLabels(LABELS).endMetadata() + .withSpec(new DeploymentConfigSpecBuilder().withReplicas(1).build()).build()).create(); + openShift.builds().inNamespace(namespace).resource(new BuildBuilder().withNewMetadata() + .withName("build-" + namespace).withLabels(LABELS).endMetadata().build()).create(); + openShift.buildConfigs().inNamespace(namespace).resource(new BuildConfigBuilder().withNewMetadata() + .withName("bc-" + namespace).withLabels(LABELS).endMetadata().build()).create(); + } + } + + @Test + void deploymentConfigsGetUsesNamespaceHeader() { + DeploymentConfig dc = template.requestBodyAndHeaders("direct:dc-get", null, + Map.of(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other", + KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, "dc-other"), + DeploymentConfig.class); + + assertNotNull(dc); + assertEquals("other", dc.getMetadata().getNamespace()); + } + + @Test + void deploymentConfigsGetUsesNamespaceOption() { + DeploymentConfig dc = template.requestBodyAndHeader("direct:dc-get-option", null, + KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, "dc-other", DeploymentConfig.class); + + assertNotNull(dc); + assertEquals("other", dc.getMetadata().getNamespace()); + } + + @Test + void deploymentConfigsHeaderWinsOverNamespaceOption() { + DeploymentConfig dc = template.requestBodyAndHeaders("direct:dc-get-option", null, + Map.of(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "test", + KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, "dc-other"), + DeploymentConfig.class); + + assertNull(dc); + } + + @Test + void deploymentConfigsListUsesNamespaceHeader() { + List<?> result = template.requestBodyAndHeader("direct:dc-list", null, + KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other", List.class); + + assertEquals(List.of("dc-other"), names(result)); + } + + @Test + void deploymentConfigsListWithoutNamespaceUsesClientNamespace() { + List<?> result = template.requestBody("direct:dc-list", null, List.class); + + assertEquals(List.of("dc-test"), names(result)); + } + + @Test + void deploymentConfigsListByLabelsUsesNamespaceHeader() { + List<?> result = template.requestBodyAndHeaders("direct:dc-list-labels", null, + Map.of(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other", + KubernetesConstants.KUBERNETES_DEPLOYMENTS_LABELS, LABELS), + List.class); + + assertEquals(List.of("dc-other"), names(result)); + } + + @Test + void deploymentConfigsListByLabelsRequiresLabels() { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBody("direct:dc-list-labels", "")); + + assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertEquals("Listing Deployment Configs by labels requires specifying labels", e.getCause().getMessage()); + } + + @Test + void deploymentConfigsCreateUsesNamespaceOption() { + DeploymentConfig dc = template.requestBodyAndHeaders("direct:dc-create-option", null, + Map.of(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, "dc-new", + KubernetesConstants.KUBERNETES_DEPLOYMENT_CONFIG_SPEC, + new DeploymentConfigSpecBuilder().withReplicas(2).build()), + DeploymentConfig.class); + + assertEquals("other", dc.getMetadata().getNamespace()); + assertNotNull(client.adapt(OpenShiftClient.class).deploymentConfigs().inNamespace("other").withName("dc-new").get()); + } + + @Test + void deploymentConfigsScaleWithoutStatusReturnsRequestedReplicas() { + Exchange exchange = template.request("direct:dc-scale", e -> { + e.getIn().setHeader(KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other"); + e.getIn().setHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_NAME, "dc-other"); + e.getIn().setHeader(KubernetesConstants.KUBERNETES_DEPLOYMENT_REPLICAS, 3); + }); + + assertNull(exchange.getException()); + assertEquals(3, exchange.getMessage().getBody(Integer.class)); + assertEquals(3, client.adapt(OpenShiftClient.class).deploymentConfigs().inNamespace("other").withName("dc-other") + .get().getSpec().getReplicas()); + } + + @Test + void buildsListUsesNamespaceHeader() { + List<?> result = template.requestBodyAndHeader("direct:builds-list", null, + KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other", List.class); + + assertEquals(List.of("build-other"), names(result)); + } + + @Test + void buildsListUsesNamespaceOption() { + List<?> result = template.requestBody("direct:builds-list-option", null, List.class); + + assertEquals(List.of("build-other"), names(result)); + } + + @Test + void buildsListWithoutNamespaceListsAllNamespaces() { + List<?> result = template.requestBody("direct:builds-list", null, List.class); + + assertEquals(List.of("build-other", "build-test"), names(result)); + } + + @Test + void buildsGetUsesNamespaceOption() { + Object build = template.requestBodyAndHeader("direct:builds-get-option", null, + KubernetesConstants.KUBERNETES_BUILD_NAME, "build-other"); + + assertEquals("other", ((HasMetadata) build).getMetadata().getNamespace()); + } + + @Test + void buildsListByLabelsRequiresLabels() { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBody("direct:builds-list-labels", "")); + + assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertEquals("Listing Builds by labels requires specifying labels", e.getCause().getMessage()); + } + + @Test + void buildConfigsListUsesNamespaceHeader() { + List<?> result = template.requestBodyAndHeader("direct:bc-list", null, + KubernetesConstants.KUBERNETES_NAMESPACE_NAME, "other", List.class); + + assertEquals(List.of("bc-other"), names(result)); + } + + @Test + void buildConfigsListUsesNamespaceOption() { + List<?> result = template.requestBody("direct:bc-list-option", null, List.class); + + assertEquals(List.of("bc-other"), names(result)); + } + + @Test + void buildConfigsListWithoutNamespaceListsAllNamespaces() { + List<?> result = template.requestBody("direct:bc-list", null, List.class); + + assertEquals(List.of("bc-other", "bc-test"), names(result)); + } + + @Test + void buildConfigsListByLabelsUsesNamespaceOption() { + List<?> result = template.requestBodyAndHeader("direct:bc-list-labels-option", null, + KubernetesConstants.KUBERNETES_BUILD_CONFIGS_LABELS, LABELS, List.class); + + assertEquals(List.of("bc-other"), names(result)); + } + + @Test + void buildConfigsListByLabelsRequiresLabels() { + CamelExecutionException e = assertThrows(CamelExecutionException.class, + () -> template.requestBody("direct:bc-list-labels", "")); + + assertInstanceOf(IllegalArgumentException.class, e.getCause()); + assertEquals("Listing Build Configs by labels requires specifying labels", e.getCause().getMessage()); + } + + private static List<String> names(List<?> resources) { + return resources.stream().map(r -> ((HasMetadata) r).getMetadata().getName()).sorted() + .collect(Collectors.toList()); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + String dc = "openshift-deploymentconfigs:///?kubernetesClient=#kubernetesClient&operation="; + from("direct:dc-get").to(dc + "getDeploymentConfig"); + from("direct:dc-get-option").to(dc + "getDeploymentConfig&namespace=other"); + from("direct:dc-list").to(dc + "listDeploymentConfigs"); + from("direct:dc-list-labels").to(dc + "listDeploymentConfigsByLabels"); + from("direct:dc-create-option").to(dc + "createDeploymentConfig&namespace=other"); + from("direct:dc-scale").to(dc + "scaleDeploymentConfig"); + + String builds = "openshift-builds:///?kubernetesClient=#kubernetesClient&operation="; + from("direct:builds-list").to(builds + "listBuilds"); + from("direct:builds-list-option").to(builds + "listBuilds&namespace=other"); + from("direct:builds-get-option").to(builds + "getBuild&namespace=other"); + from("direct:builds-list-labels").to(builds + "listBuildsByLabels"); + + String bc = "openshift-build-configs:///?kubernetesClient=#kubernetesClient&operation="; + from("direct:bc-list").to(bc + "listBuildConfigs"); + from("direct:bc-list-option").to(bc + "listBuildConfigs&namespace=other"); + from("direct:bc-list-labels").to(bc + "listBuildConfigsByLabels"); + from("direct:bc-list-labels-option").to(bc + "listBuildConfigsByLabels&namespace=other"); + } + }; + } +} 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..ae39be622221 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,25 @@ 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 openshift producers use the namespace header and option in every operation + +The `openshift-builds`, `openshift-build-configs` and `openshift-deploymentconfigs` producers now take the namespace +from the `CamelKubernetesNamespaceName` header, or else from the `namespace` option, in every operation: + +* `listBuilds` and `listBuildConfigs` list only that namespace. Before, they listed all namespaces even when a + namespace was set. Without a namespace they still list all namespaces. +* `listDeploymentConfigs`, `listDeploymentConfigsByLabels` and `getDeploymentConfig` use that namespace. Before, they + always used the namespace of the Kubernetes client. +* The `namespace` option was used by the `openshift-deploymentconfigs` consumer only. A producer with the option set + now uses it when the header is not set. + +`listBuildsByLabels`, `listBuildConfigsByLabels` and `listDeploymentConfigsByLabels` without the labels header fail +with an `IllegalArgumentException` that says the labels are required, instead of a `NullPointerException`. + +`scaleDeploymentConfig` returns the requested number of replicas. Before, it returned the replicas of the status of the +deployment config, which may not have the new count yet, and failed with a `NullPointerException` when the deployment +config 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`
