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

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


The following commit(s) were added to refs/heads/main by this push:
     new 3a491c44b feat(trait): gateway
3a491c44b is described below

commit 3a491c44b317c3834ba71850e59872e4d9fb7e53
Author: Pasquale Congiusti <[email protected]>
AuthorDate: Sat Mar 21 16:16:27 2026 +0100

    feat(trait): gateway
    
    Closes #5072
---
 .github/workflows/gateway.yml                      |  99 ++++++++++
 docs/modules/ROOT/partials/apis/camel-k-crds.adoc  |  41 +++++
 e2e/gateway/files/PlatformHttpServer.java          |  26 +++
 e2e/gateway/gateway_test.go                        |  74 ++++++++
 e2e/gateway/setup/gateway.yaml                     |  23 +++
 e2e/gateway/setup/setup.sh                         |  28 +++
 e2e/support/test_support.go                        |  50 ++++-
 go.mod                                             |   1 +
 helm/camel-k/crds/camel-k-crds.yaml                | 144 +++++++++++++++
 pkg/apis/addtoscheme_gateway.go                    |  27 +++
 pkg/apis/camel/v1/common_types.go                  |   2 +
 pkg/apis/camel/v1/trait/gateway.go                 |  36 ++++
 pkg/apis/camel/v1/trait/zz_generated.deepcopy.go   |  16 ++
 pkg/apis/camel/v1/zz_generated.deepcopy.go         |   5 +
 .../camel/applyconfiguration/camel/v1/traits.go    |  10 +
 pkg/client/client.go                               |   3 +-
 .../camel.apache.org_integrationplatforms.yaml     |  36 ++++
 .../camel.apache.org_integrationprofiles.yaml      |  36 ++++
 .../crd/bases/camel.apache.org_integrations.yaml   |  36 ++++
 .../config/crd/bases/camel.apache.org_pipes.yaml   |  36 ++++
 .../rbac/descoped/operator-cluster-role.yaml       |  12 ++
 .../config/rbac/namespaced/operator-role.yaml      |  12 ++
 pkg/trait/gateway.go                               | 205 +++++++++++++++++++++
 pkg/trait/gateway_test.go                          | 162 ++++++++++++++++
 pkg/trait/trait_register.go                        |   1 +
 script/Makefile                                    |   6 +
 26 files changed, 1121 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/gateway.yml b/.github/workflows/gateway.yml
new file mode 100644
index 000000000..052efde3a
--- /dev/null
+++ b/.github/workflows/gateway.yml
@@ -0,0 +1,99 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+name: gateway
+
+env:
+  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+on:
+  pull_request:
+    branches:
+      - main
+      - "release-*"
+    paths-ignore:
+      - 'docs/**'
+      - 'java/**'
+      - 'proposals/**'
+      - '**.adoc'
+      - '**.md'
+      - 'KEYS'
+      - 'LICENSE'
+      - 'NOTICE'
+  push:
+    branches:
+      - main
+      - "release-*"
+    paths-ignore:
+      - 'docs/**'
+      - 'java/**'
+      - 'proposals/**'
+      - '**.adoc'
+      - '**.md'
+      - 'KEYS'
+      - 'LICENSE'
+      - 'NOTICE'
+  workflow_dispatch:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || 
github.sha }}
+  cancel-in-progress: true
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+
+    - name: Checkout code
+      uses: actions/checkout@v4
+      with:
+        persist-credentials: false
+        submodules: recursive
+
+    - name: Infra setting
+      uses: ./.github/actions/infra-setting
+
+    - name: Install Envoy
+      shell: bash
+      run: |
+        ./e2e/gateway/setup/setup.sh
+
+    - name: Install operator
+      shell: bash
+      run: |
+        kubectl create ns camel-k
+        make install-k8s-global
+        kubectl wait --for=jsonpath='{.status.phase}'=Ready itp camel-k -n 
camel-k --timeout=60s
+
+    - name: Run test
+      shell: bash
+      run: |
+        set -euo pipefail
+        # Cleanup function to stop tunnel
+        cleanup() {
+          echo "** Stopping Minikube tunnel"
+          if [[ -n "${TUNNEL_PID-}" ]]; then
+            kill "$TUNNEL_PID" || true
+          fi
+        }
+        trap cleanup EXIT
+
+        echo "** Starting Minikube tunnel (requires sudo)"
+        minikube tunnel &
+        TUNNEL_PID=$!
+
+        DO_TEST_PREBUILD=false GOTESTFMT="-json 2>&1 | gotestfmt" make 
test-gateway
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc 
b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index ed08e799a..43a395ddf 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -6046,6 +6046,13 @@ The configuration of Error Handler trait.
 
 Deprecated: no longer in use.
 
+|`gateway` +
+*xref:#_camel_apache_org_v1_trait_GatewayTrait[GatewayTrait]*
+|
+
+
+The configuration of Istio trait
+
 |`gc` +
 *xref:#_camel_apache_org_v1_trait_GCTrait[GCTrait]*
 |
@@ -7328,6 +7335,39 @@ Discovery client cache to be used, either `disabled`, 
`disk` or `memory` (defaul
 Deprecated: no longer in use.
 
 
+|===
+
+[#_camel_apache_org_v1_trait_GatewayTrait]
+=== GatewayTrait
+
+*Appears on:*
+
+* <<#_camel_apache_org_v1_Traits, Traits>>
+
+The Gateway trait can be used to expose the service associated with the 
integration
+to the outside world with a Kubernetes Gateway API.
+
+
+[cols="2,2a",options="header"]
+|===
+|Field
+|Description
+
+|`Trait` +
+*xref:#_camel_apache_org_v1_trait_Trait[Trait]*
+|(Members of `Trait` are embedded into this type.)
+
+
+
+
+|`className` +
+string
+|
+
+
+The class name to use for the gateway configuration.
+
+
 |===
 
 [#_camel_apache_org_v1_trait_GitOpsTrait]
@@ -9688,6 +9728,7 @@ The list of taints to tolerate, in the form 
`Key[=Value]:Effect[:Seconds]`
 * <<#_camel_apache_org_v1_trait_AffinityTrait, AffinityTrait>>
 * <<#_camel_apache_org_v1_trait_CronTrait, CronTrait>>
 * <<#_camel_apache_org_v1_trait_GCTrait, GCTrait>>
+* <<#_camel_apache_org_v1_trait_GatewayTrait, GatewayTrait>>
 * <<#_camel_apache_org_v1_trait_GitOpsTrait, GitOpsTrait>>
 * <<#_camel_apache_org_v1_trait_HealthTrait, HealthTrait>>
 * <<#_camel_apache_org_v1_trait_IngressTrait, IngressTrait>>
diff --git a/e2e/gateway/files/PlatformHttpServer.java 
b/e2e/gateway/files/PlatformHttpServer.java
new file mode 100644
index 000000000..31d8d19f3
--- /dev/null
+++ b/e2e/gateway/files/PlatformHttpServer.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class PlatformHttpServer extends RouteBuilder {
+    @Override
+    public void configure() throws Exception {
+        from("platform-http:/hello?httpMethodRestrict=GET")
+            .setBody(simple("Hello ${header.name}"));
+    }
+}
diff --git a/e2e/gateway/gateway_test.go b/e2e/gateway/gateway_test.go
new file mode 100644
index 000000000..cea4e616e
--- /dev/null
+++ b/e2e/gateway/gateway_test.go
@@ -0,0 +1,74 @@
+//go:build integration
+// +build integration
+
+// To enable compilation of this file in Goland, go to "Settings -> Go -> 
Vendoring & Build Tags -> Custom Tags" and add "integration"
+
+/*
+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 gateway
+
+import (
+       "context"
+       "os/exec"
+       "testing"
+
+       . "github.com/apache/camel-k/v2/e2e/support"
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       . "github.com/onsi/gomega"
+       corev1 "k8s.io/api/core/v1"
+)
+
+func TestGatewayTrait(t *testing.T) {
+       t.Parallel()
+       WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
+               g.Expect(KamelRun(t, ctx, ns, "files/PlatformHttpServer.java",
+                       "-t", "gateway.enabled=true",
+                       "-t", "gateway.class-name=envoy",
+               ).Execute()).To(Succeed())
+               g.Eventually(IntegrationConditionStatus(t, ctx, ns, 
"platform-http-server",
+                       v1.IntegrationConditionReady), 
TestTimeoutMedium).Should(Equal(corev1.ConditionTrue))
+               g.Eventually(Gateway(t, ctx, ns, "platform-http-server"), 
TestTimeoutShort).Should(Not(BeNil()))
+               // Wait for the address to be assigned
+               var gwAddress string
+
+               // IMPORTANT NOTE: this test would likely fail if the Envoy 
gateway is not able
+               // to assign an address correctly. In our case we need to make 
sure to run
+               // `minikube tunnel` before running this test. It requires sudo.
+
+               g.Eventually(func() string {
+                       gw := Gateway(t, ctx, ns, "platform-http-server")()
+                       if gw == nil || len(gw.Status.Addresses) == 0 {
+                               return ""
+                       }
+                       gwAddress = string(gw.Status.Addresses[0].Value)
+
+                       return gwAddress
+               }, TestTimeoutShort).ShouldNot(BeEmpty(), "expected gateway to 
have an assigned address")
+
+               g.Eventually(func() (string, error) {
+                       cmd := exec.Command("curl",
+                               "-s",
+                               "-H", "name: test!",
+                               "http://"+gwAddress+":8080/hello";,
+                       )
+                       out, err := cmd.CombinedOutput()
+
+                       return string(out), err
+               }, TestTimeoutMedium).Should(ContainSubstring("Hello test!"))
+       })
+}
diff --git a/e2e/gateway/setup/gateway.yaml b/e2e/gateway/setup/gateway.yaml
new file mode 100644
index 000000000..be3172bad
--- /dev/null
+++ b/e2e/gateway/setup/gateway.yaml
@@ -0,0 +1,23 @@
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+  name: envoy
+spec:
+  controllerName: gateway.envoyproxy.io/gatewayclass-controller
diff --git a/e2e/gateway/setup/setup.sh b/e2e/gateway/setup/setup.sh
new file mode 100755
index 000000000..ce647c12e
--- /dev/null
+++ b/e2e/gateway/setup/setup.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# ---------------------------------------------------------------------------
+# 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.
+# ---------------------------------------------------------------------------
+
+TIMEOUT="150s"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+kubectl apply -f 
https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml
 --server-side
+kubectl apply -f 
https://github.com/envoyproxy/gateway/releases/latest/download/install.yaml 
--server-side
+kubectl wait --for=condition=available deployment/envoy-gateway -n 
envoy-gateway-system --timeout=$TIMEOUT
+
+# Install gateway classname (not available by default in envoy installation)
+kubectl apply -f $SCRIPT_DIR/gateway.yaml
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index 77f86c854..00203f8cf 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -64,6 +64,7 @@ import (
        "k8s.io/apimachinery/pkg/types"
        "k8s.io/client-go/rest"
        "k8s.io/utils/ptr"
+       gwv1 "sigs.k8s.io/gateway-api/apis/v1"
 
        ctrl "sigs.k8s.io/controller-runtime/pkg/client"
        "sigs.k8s.io/controller-runtime/pkg/client/apiutil"
@@ -153,7 +154,6 @@ func TestContext() context.Context {
 }
 
 func TestClient(t *testing.T) client.Client {
-
        if testClient != nil {
                return testClient
        }
@@ -1055,7 +1055,7 @@ func IntegrationSpecProfile(t *testing.T, ctx 
context.Context, ns string, name s
 func IntegrationStatusCapabilities(t *testing.T, ctx context.Context, ns 
string, name string) func() []string {
        return func() []string {
                it := Integration(t, ctx, ns, name)()
-               if it == nil || &it.Status == nil {
+               if it == nil {
                        return nil
                }
                return it.Status.Capabilities
@@ -1910,7 +1910,7 @@ func BuildPhase(t *testing.T, ctx context.Context, ns, 
name string) func() v1.Bu
 func BuildConditions(t *testing.T, ctx context.Context, ns, name string) 
func() []v1.BuildCondition {
        return func() []v1.BuildCondition {
                build := Build(t, ctx, ns, name)()
-               if build != nil && &build.Status != nil && 
build.Status.Conditions != nil {
+               if build != nil && build.Status.Conditions != nil {
                        return build.Status.Conditions
                }
                return nil
@@ -1920,7 +1920,7 @@ func BuildConditions(t *testing.T, ctx context.Context, 
ns, name string) func()
 func BuildCondition(t *testing.T, ctx context.Context, ns string, name string, 
conditionType v1.BuildConditionType) func() *v1.BuildCondition {
        return func() *v1.BuildCondition {
                build := Build(t, ctx, ns, name)()
-               if build != nil && &build.Status != nil && 
build.Status.Conditions != nil {
+               if build != nil && build.Status.Conditions != nil {
                        return build.Status.GetCondition(conditionType)
                }
                return &v1.BuildCondition{}
@@ -3129,3 +3129,45 @@ func ScaledObject(t *testing.T, ctx context.Context, ns, 
name string) func() *ke
                return &scaledObject
        }
 }
+
+// MinikubeTunnel is used to temporarily tunnel Minikube and return a function 
to stop after it's used.
+func MinikubeTunnel(t *testing.T, ctx context.Context) func() {
+       log.Info("** Started Minikube tunnel")
+       cmd := exec.CommandContext(ctx,
+               "minikube", "tunnel",
+       )
+
+       if err := cmd.Start(); err != nil {
+               t.Fatalf("failed to start minikube tunnel: %v", err)
+       }
+
+       // Give tunnel a moment to establish
+       time.Sleep(2 * time.Second)
+
+       // Return a cleanup function
+       return func() {
+               log.Info("** Stopping Minikube tunnel")
+               if err := cmd.Process.Kill(); err != nil {
+                       t.Logf("failed to kill minikube tunnel: %v", err)
+               }
+       }
+}
+
+// Gateway returns the gateway with the given name.
+func Gateway(t *testing.T, ctx context.Context, ns string, name string) func() 
*gwv1.Gateway {
+       return func() *gwv1.Gateway {
+               gw := gwv1.Gateway{}
+               key := ctrl.ObjectKey{
+                       Namespace: ns,
+                       Name:      name,
+               }
+               err := TestClient(t).Get(ctx, key, &gw)
+               if err != nil && k8serrors.IsNotFound(err) {
+                       return nil
+               } else if err != nil {
+                       failTest(t, err)
+               }
+
+               return &gw
+       }
+}
diff --git a/go.mod b/go.mod
index e3435c29e..7f05aab57 100644
--- a/go.mod
+++ b/go.mod
@@ -51,6 +51,7 @@ require (
        knative.dev/pkg v0.0.0-20260120122510-4a022ed9999a
        knative.dev/serving v0.48.1
        sigs.k8s.io/controller-runtime v0.23.3
+       sigs.k8s.io/gateway-api v1.1.0
        sigs.k8s.io/structured-merge-diff/v6 v6.3.2
 )
 
diff --git a/helm/camel-k/crds/camel-k-crds.yaml 
b/helm/camel-k/crds/camel-k-crds.yaml
index 1d1c28c75..5cc4e45b8 100644
--- a/helm/camel-k/crds/camel-k-crds.yaml
+++ b/helm/camel-k/crds/camel-k-crds.yaml
@@ -4362,6 +4362,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -6863,6 +6881,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -9256,6 +9292,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -11635,6 +11689,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -20875,6 +20947,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -23214,6 +23304,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -33817,6 +33925,24 @@ spec:
                               in application properties
                             type: string
                         type: object
+                      gateway:
+                        description: The configuration of Istio trait
+                        properties:
+                          className:
+                            description: The class name to use for the gateway 
configuration.
+                            type: string
+                          configuration:
+                            description: |-
+                              Legacy trait configuration parameters.
+
+                              Deprecated: for backward compatibility.
+                            type: object
+                            x-kubernetes-preserve-unknown-fields: true
+                          enabled:
+                            description: Can be used to enable or disable a 
trait.
+                              All traits share this common property.
+                            type: boolean
+                        type: object
                       gc:
                         description: The configuration of GC trait
                         properties:
@@ -36083,6 +36209,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
diff --git a/pkg/apis/addtoscheme_gateway.go b/pkg/apis/addtoscheme_gateway.go
new file mode 100644
index 000000000..1cbae8e65
--- /dev/null
+++ b/pkg/apis/addtoscheme_gateway.go
@@ -0,0 +1,27 @@
+/*
+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 apis
+
+import (
+       gwv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+func init() {
+       // Register the types with the Scheme so the components can map objects 
to GroupVersionKinds and back
+       AddToSchemes = append(AddToSchemes, gwv1.Install)
+}
diff --git a/pkg/apis/camel/v1/common_types.go 
b/pkg/apis/camel/v1/common_types.go
index da4ccd4cb..9c23e52af 100644
--- a/pkg/apis/camel/v1/common_types.go
+++ b/pkg/apis/camel/v1/common_types.go
@@ -210,6 +210,8 @@ type Traits struct {
        //
        // Deprecated: no longer in use.
        ErrorHandler *trait.ErrorHandlerTrait `json:"error-handler,omitempty" 
property:"error-handler"`
+       // The configuration of Istio trait
+       Gateway *trait.GatewayTrait `json:"gateway,omitempty" 
property:"gateway"`
        // The configuration of GC trait
        GC *trait.GCTrait `json:"gc,omitempty" property:"gc"`
        // The configuration of GitOps trait
diff --git a/pkg/apis/camel/v1/trait/gateway.go 
b/pkg/apis/camel/v1/trait/gateway.go
new file mode 100644
index 000000000..131048542
--- /dev/null
+++ b/pkg/apis/camel/v1/trait/gateway.go
@@ -0,0 +1,36 @@
+/*
+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 trait
+
+// The Gateway trait can be used to expose the service associated with the 
Integration
+// to the outside world with a Kubernetes Gateway API. The trait is in charge 
to automatically discover associate the
+// Integration Service generated with a Gateway and an HTTPRoute resource 
(HTTP/HTTPS protocol only supported).
+//
+// NOTE: if any other protocol is required, please create a request in order 
to develop it.
+//
+// +camel-k:trait=gateway.
+//
+//nolint:godoclint
+type GatewayTrait struct {
+       Trait `json:",inline" property:",squash"`
+
+       // The class name to use for the gateway configuration.
+       ClassName string `json:"className,omitempty" property:"class-name"`
+       // The listeners in the format "port;protocol" (default, "8080;HTTP").
+       Listeners []string `json:"listeners,omitempty" property:"listeners"`
+}
diff --git a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go 
b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
index 8cd29b192..563b464bd 100644
--- a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
@@ -440,6 +440,22 @@ func (in *GCTrait) DeepCopy() *GCTrait {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
+func (in *GatewayTrait) DeepCopyInto(out *GatewayTrait) {
+       *out = *in
+       in.Trait.DeepCopyInto(&out.Trait)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new GatewayTrait.
+func (in *GatewayTrait) DeepCopy() *GatewayTrait {
+       if in == nil {
+               return nil
+       }
+       out := new(GatewayTrait)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, 
writing into out. in must be non-nil.
 func (in *GitOpsTrait) DeepCopyInto(out *GitOpsTrait) {
        *out = *in
diff --git a/pkg/apis/camel/v1/zz_generated.deepcopy.go 
b/pkg/apis/camel/v1/zz_generated.deepcopy.go
index edfe45e01..875c8413e 100644
--- a/pkg/apis/camel/v1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1/zz_generated.deepcopy.go
@@ -3231,6 +3231,11 @@ func (in *Traits) DeepCopyInto(out *Traits) {
                *out = new(trait.ErrorHandlerTrait)
                (*in).DeepCopyInto(*out)
        }
+       if in.Gateway != nil {
+               in, out := &in.Gateway, &out.Gateway
+               *out = new(trait.GatewayTrait)
+               (*in).DeepCopyInto(*out)
+       }
        if in.GC != nil {
                in, out := &in.GC, &out.GC
                *out = new(trait.GCTrait)
diff --git a/pkg/client/camel/applyconfiguration/camel/v1/traits.go 
b/pkg/client/camel/applyconfiguration/camel/v1/traits.go
index d86eece44..7f5ca128a 100644
--- a/pkg/client/camel/applyconfiguration/camel/v1/traits.go
+++ b/pkg/client/camel/applyconfiguration/camel/v1/traits.go
@@ -50,6 +50,8 @@ type TraitsApplyConfiguration struct {
        //
        // Deprecated: no longer in use.
        ErrorHandler *trait.ErrorHandlerTrait `json:"error-handler,omitempty"`
+       // The configuration of Istio trait
+       Gateway *trait.GatewayTrait `json:"gateway,omitempty"`
        // The configuration of GC trait
        GC *trait.GCTrait `json:"gc,omitempty"`
        // The configuration of GitOps trait
@@ -218,6 +220,14 @@ func (b *TraitsApplyConfiguration) WithErrorHandler(value 
trait.ErrorHandlerTrai
        return b
 }
 
+// WithGateway sets the Gateway field in the declarative configuration to the 
given value
+// and returns the receiver, so that objects can be built by chaining "With" 
function invocations.
+// If called multiple times, the Gateway field is set to the value of the last 
call.
+func (b *TraitsApplyConfiguration) WithGateway(value trait.GatewayTrait) 
*TraitsApplyConfiguration {
+       b.Gateway = &value
+       return b
+}
+
 // WithGC sets the GC field in the declarative configuration to the given value
 // and returns the receiver, so that objects can be built by chaining "With" 
function invocations.
 // If called multiple times, the GC field is set to the value of the last call.
diff --git a/pkg/client/client.go b/pkg/client/client.go
index 27ad8a038..db15ff9d3 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -110,7 +110,7 @@ func (c *defaultClient) GetCurrentNamespace(kubeConfig 
string) (string, error) {
 func NewOutOfClusterClient(kubeconfig string) (Client, error) {
        initialize(kubeconfig)
        // using fast discovery from outside the cluster
-       return NewClient(true)
+       return NewClient(false)
 }
 
 // NewClient creates a new k8s client that can be used from outside or in the 
cluster.
@@ -134,7 +134,6 @@ func NewClientWithConfig(fastDiscovery bool, cfg 
*rest.Config) (Client, error) {
        var err error
        clientScheme := scheme.Scheme
        if !clientScheme.IsVersionRegistered(v1.SchemeGroupVersion) {
-               // Setup Scheme for all resources
                err = apis.AddToScheme(clientScheme)
                if err != nil {
                        return nil, err
diff --git 
a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml 
b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
index e0945c82a..9ae9a9666 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
@@ -1078,6 +1078,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -3579,6 +3597,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
diff --git 
a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml 
b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
index 425b16dad..15b66ec5c 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
@@ -936,6 +936,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -3315,6 +3333,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
diff --git a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml 
b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
index f87fb884b..7b8e85c99 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrations.yaml
@@ -7786,6 +7786,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
@@ -10125,6 +10143,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
diff --git a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml 
b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
index 6b307e25d..880fb7179 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
@@ -7841,6 +7841,24 @@ spec:
                               in application properties
                             type: string
                         type: object
+                      gateway:
+                        description: The configuration of Istio trait
+                        properties:
+                          className:
+                            description: The class name to use for the gateway 
configuration.
+                            type: string
+                          configuration:
+                            description: |-
+                              Legacy trait configuration parameters.
+
+                              Deprecated: for backward compatibility.
+                            type: object
+                            x-kubernetes-preserve-unknown-fields: true
+                          enabled:
+                            description: Can be used to enable or disable a 
trait.
+                              All traits share this common property.
+                            type: boolean
+                        type: object
                       gc:
                         description: The configuration of GC trait
                         properties:
@@ -10107,6 +10125,24 @@ spec:
                           in application properties
                         type: string
                     type: object
+                  gateway:
+                    description: The configuration of Istio trait
+                    properties:
+                      className:
+                        description: The class name to use for the gateway 
configuration.
+                        type: string
+                      configuration:
+                        description: |-
+                          Legacy trait configuration parameters.
+
+                          Deprecated: for backward compatibility.
+                        type: object
+                        x-kubernetes-preserve-unknown-fields: true
+                      enabled:
+                        description: Can be used to enable or disable a trait. 
All
+                          traits share this common property.
+                        type: boolean
+                    type: object
                   gc:
                     description: The configuration of GC trait
                     properties:
diff --git a/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml 
b/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
index 8a537581b..8ed6ee7ef 100644
--- a/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
+++ b/pkg/resources/config/rbac/descoped/operator-cluster-role.yaml
@@ -182,6 +182,18 @@ rules:
   - get
   - list
   - patch
+# Required by gateway trait
+- apiGroups:
+  - gateway.networking.k8s.io
+  resources:
+  - gateways
+  - httproutes
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
 # Roles and RoleBindings
 - apiGroups:
   - rbac.authorization.k8s.io
diff --git a/pkg/resources/config/rbac/namespaced/operator-role.yaml 
b/pkg/resources/config/rbac/namespaced/operator-role.yaml
index 27a264bee..241087014 100644
--- a/pkg/resources/config/rbac/namespaced/operator-role.yaml
+++ b/pkg/resources/config/rbac/namespaced/operator-role.yaml
@@ -163,6 +163,18 @@ rules:
   - get
   - list
   - patch
+# Required by gateway trait
+- apiGroups:
+  - gateway.networking.k8s.io
+  resources:
+  - gateways
+  - httproutes
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
 # Required by mount trait
 - apiGroups:
   - ""
diff --git a/pkg/trait/gateway.go b/pkg/trait/gateway.go
new file mode 100644
index 000000000..2c5b30664
--- /dev/null
+++ b/pkg/trait/gateway.go
@@ -0,0 +1,205 @@
+/*
+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 trait
+
+import (
+       "errors"
+       "fmt"
+       "strconv"
+       "strings"
+
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/utils/ptr"
+       gwv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+const (
+       gatewayTraitID    = "gateway"
+       gatewayTraitOrder = 2420
+
+       gatewayDefaultListener = "8080;HTTP"
+)
+
+type gatewayTrait struct {
+       BaseTrait
+       traitv1.GatewayTrait `property:",squash"`
+}
+
+func newGatewayTrait() Trait {
+       return &gatewayTrait{
+               BaseTrait: NewBaseTrait(gatewayTraitID, gatewayTraitOrder),
+       }
+}
+
+func (t *gatewayTrait) Configure(e *Environment) (bool, *TraitCondition, 
error) {
+       if e.Integration == nil || !ptr.Deref(t.Enabled, false) || 
!e.IntegrationInRunningPhases() {
+               return false, nil, nil
+       }
+
+       if e.Resources.GetUserServiceForIntegration(e.Integration) == nil {
+               return false, NewIntegrationCondition(
+                       "Gateway",
+                       v1.IntegrationConditionServiceAvailable,
+                       corev1.ConditionFalse,
+                       v1.IntegrationConditionServiceNotAvailableReason,
+                       "No service available. Skipping the trait execution",
+               ), nil
+       }
+
+       return true, nil, nil
+}
+
+func (t *gatewayTrait) Apply(e *Environment) error {
+       service := e.Resources.GetUserServiceForIntegration(e.Integration)
+       gwName := e.Integration.GetName()
+
+       gw, err := buildGateway(gwName, e.Integration.GetNamespace(), 
t.ClassName, t.getListeners())
+       if err != nil {
+               return err
+       }
+       e.Resources.Add(gw)
+       servicePorts := extractPorts(service.Spec.Ports)
+       route := buildHTTPRoute(gwName, gw.GetName(), service.GetName(), 
gw.GetNamespace(), servicePorts)
+       e.Resources.Add(route)
+
+       e.Integration.Status.SetCondition(
+               v1.IntegrationConditionExposureAvailable,
+               corev1.ConditionTrue,
+               "GatewayAvailable",
+               "Service is exposed via a Gateway and HTTPRoute named "+gwName,
+       )
+
+       return nil
+}
+
+func (t *gatewayTrait) getListeners() []string {
+       if t.Listeners != nil {
+               return t.Listeners
+       }
+
+       return []string{gatewayDefaultListener}
+}
+
+// buildGateway provides the gateway with the associated listeners.
+func buildGateway(name, namespace, className string, listeners []string) 
(*gwv1.Gateway, error) {
+       gwListeners := make([]gwv1.Listener, 0, len(listeners))
+
+       for _, l := range listeners {
+               parts := strings.Split(l, ";")
+               if len(parts) != 2 {
+                       return nil, errors.New("could not parse gateway 
listener " + l)
+               }
+
+               port32, err := strconv.ParseInt(parts[0], 10, 32)
+               if err != nil {
+                       return nil, errors.New("could not parse gateway port " 
+ parts[0])
+               }
+               port := int32(port32)
+               protocol := strings.ToUpper(parts[1])
+               if !isSupported(protocol) {
+                       return nil, errors.New("protocol gateway " + protocol + 
" is not yet supported: open change request issue to project tracking")
+               }
+
+               listenerName := fmt.Sprintf("%s-%d", name, port)
+               gwListeners = append(gwListeners, gwv1.Listener{
+                       Name:     gwv1.SectionName(listenerName),
+                       Port:     gwv1.PortNumber(port),
+                       Protocol: gwv1.ProtocolType(protocol),
+                       AllowedRoutes: &gwv1.AllowedRoutes{
+                               Namespaces: &gwv1.RouteNamespaces{
+                                       From: ptr.To(gwv1.NamespacesFromSame),
+                               },
+                       },
+               })
+       }
+
+       return &gwv1.Gateway{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: gwv1.SchemeGroupVersion.String(),
+                       Kind:       "Gateway",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      name,
+                       Namespace: namespace,
+               },
+               Spec: gwv1.GatewaySpec{
+                       GatewayClassName: gwv1.ObjectName(className),
+                       Listeners:        gwListeners,
+               },
+       }, nil
+}
+
+func isSupported(protocol string) bool {
+       return protocol == "HTTP" || protocol == "HTTPS"
+}
+
+// buildHTTPRoute provides the most basic gateway builder method.
+func buildHTTPRoute(routeName, gatewayName, serviceName, namespace string, 
servicePorts []int32) *gwv1.HTTPRoute {
+       rules := make([]gwv1.HTTPRouteRule, 0, len(servicePorts))
+
+       for _, p := range servicePorts {
+               port := gwv1.PortNumber(p)
+               rule := gwv1.HTTPRouteRule{
+                       BackendRefs: []gwv1.HTTPBackendRef{
+                               {
+                                       BackendRef: gwv1.BackendRef{
+                                               BackendObjectReference: 
gwv1.BackendObjectReference{
+                                                       Name: 
gwv1.ObjectName(serviceName),
+                                                       Port: ptr.To(port),
+                                               },
+                                       },
+                               },
+                       },
+               }
+
+               rules = append(rules, rule)
+       }
+
+       return &gwv1.HTTPRoute{
+               TypeMeta: metav1.TypeMeta{
+                       APIVersion: gwv1.SchemeGroupVersion.String(),
+                       Kind:       "HTTPRoute",
+               },
+               ObjectMeta: metav1.ObjectMeta{
+                       Name:      routeName,
+                       Namespace: namespace,
+               },
+               Spec: gwv1.HTTPRouteSpec{
+                       CommonRouteSpec: gwv1.CommonRouteSpec{
+                               ParentRefs: []gwv1.ParentReference{
+                                       {
+                                               Name: 
gwv1.ObjectName(gatewayName),
+                                       },
+                               },
+                       },
+                       Rules: rules,
+               },
+       }
+}
+
+func extractPorts(ports []corev1.ServicePort) []int32 {
+       result := make([]int32, 0, len(ports))
+       for _, p := range ports {
+               result = append(result, p.Port)
+       }
+
+       return result
+}
diff --git a/pkg/trait/gateway_test.go b/pkg/trait/gateway_test.go
new file mode 100644
index 000000000..50da8dfc7
--- /dev/null
+++ b/pkg/trait/gateway_test.go
@@ -0,0 +1,162 @@
+/*
+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 trait
+
+import (
+       "testing"
+
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       "k8s.io/utils/ptr"
+       gwv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+func TestConfigureGatewayTraitDoesSucceed(t *testing.T) {
+       gwTrait, environment := createNominalGatewayTest()
+       gwTrait.ClassName = "my-gw-class"
+       configured, condition, err := gwTrait.Configure(environment)
+
+       require.NoError(t, err)
+       assert.True(t, configured)
+       assert.Nil(t, condition)
+       err = gwTrait.Apply(environment)
+       require.NoError(t, err)
+
+       // Assert gateway resource
+       var gateway gwv1.Gateway
+       environment.Resources.Visit(func(o runtime.Object) {
+               if conv, ok := o.(*gwv1.Gateway); ok {
+                       gateway = *conv
+                       return
+               }
+       })
+       assert.NotNil(t, gateway, "Could not find any generated Gateway")
+       assert.Equal(t, "integration-name", gateway.Name)
+       assert.Equal(t, gwv1.ObjectName("my-gw-class"), 
gateway.Spec.GatewayClassName)
+       assert.Len(t, gateway.Spec.Listeners, 1)
+       assert.Equal(t, gwv1.ProtocolType("HTTP"), 
gateway.Spec.Listeners[0].Protocol)
+       assert.Equal(t, gwv1.PortNumber(8080), gateway.Spec.Listeners[0].Port)
+
+       // Assert HTTPRoute resource
+       var httpRoute gwv1.HTTPRoute
+       environment.Resources.Visit(func(o runtime.Object) {
+               if conv, ok := o.(*gwv1.HTTPRoute); ok {
+                       httpRoute = *conv
+                       return
+               }
+       })
+       assert.NotNil(t, httpRoute, "Could not find any generated HTTPRoute")
+       assert.Len(t, httpRoute.Spec.ParentRefs, 1)
+       assert.Equal(t, gwv1.ObjectName(gateway.Name), 
httpRoute.Spec.ParentRefs[0].Name)
+       assert.Len(t, httpRoute.Spec.Rules, 2)
+       assert.Contains(t, httpRoute.Spec.Rules,
+               gwv1.HTTPRouteRule{
+                       BackendRefs: []gwv1.HTTPBackendRef{
+                               {BackendRef: 
gwv1.BackendRef{BackendObjectReference: gwv1.BackendObjectReference{
+                                       Name: "service-name", Port: 
ptr.To(gwv1.PortNumber(1234)),
+                               }}},
+                       },
+               },
+       )
+       assert.Contains(t, httpRoute.Spec.Rules,
+               gwv1.HTTPRouteRule{
+                       BackendRefs: []gwv1.HTTPBackendRef{
+                               {BackendRef: 
gwv1.BackendRef{BackendObjectReference: gwv1.BackendObjectReference{
+                                       Name: "service-name", Port: 
ptr.To(gwv1.PortNumber(5678)),
+                               }}},
+                       },
+               },
+       )
+
+       // Verify Integration condition as well
+       assert.NotNil(t, 
environment.Integration.Status.GetCondition(v1.IntegrationConditionExposureAvailable))
+       assert.Equal(t, corev1.ConditionTrue, 
environment.Integration.Status.GetCondition(v1.IntegrationConditionExposureAvailable).Status)
+       assert.Equal(t, "Service is exposed via a Gateway and HTTPRoute named 
integration-name",
+               
environment.Integration.Status.GetCondition(v1.IntegrationConditionExposureAvailable).Message)
+}
+
+func TestConfigureGatewayTraitMissingService(t *testing.T) {
+       gwTrait, environment := createNominalGatewayTest()
+       gwTrait.ClassName = "my-gw-class"
+       environment.Resources.Remove(func(o runtime.Object) bool {
+               if _, ok := o.(*corev1.Service); ok {
+                       return true
+               }
+
+               return false
+       })
+       configured, condition, err := gwTrait.Configure(environment)
+
+       require.NoError(t, err)
+       assert.False(t, configured)
+       assert.NotNil(t, condition)
+       assert.Contains(t, condition.message, "No service available")
+}
+
+func createNominalGatewayTest() (*gatewayTrait, *Environment) {
+       trait, _ := newGatewayTrait().(*gatewayTrait)
+       trait.Enabled = ptr.To(true)
+
+       environment := &Environment{
+               Catalog: NewCatalog(nil),
+               Integration: &v1.Integration{
+                       ObjectMeta: metav1.ObjectMeta{
+                               Name: "integration-name",
+                       },
+                       Status: v1.IntegrationStatus{
+                               Phase: v1.IntegrationPhaseDeploying,
+                       },
+               },
+               Resources: kubernetes.NewCollection(
+                       &corev1.Service{
+                               TypeMeta: metav1.TypeMeta{
+                                       Kind:       "Service",
+                                       APIVersion: "v1",
+                               },
+                               ObjectMeta: metav1.ObjectMeta{
+                                       Name:      "service-name",
+                                       Namespace: "namespace",
+                                       Labels: map[string]string{
+                                               v1.IntegrationLabel:            
 "integration-name",
+                                               
"camel.apache.org/service.type": v1.ServiceTypeUser,
+                                       },
+                               },
+                               Spec: corev1.ServiceSpec{
+                                       Ports: []corev1.ServicePort{
+                                               {
+                                                       Port: 1234,
+                                               },
+                                               {
+                                                       Port: 5678,
+                                               },
+                                       },
+                                       Selector: map[string]string{
+                                               v1.IntegrationLabel: 
"integration-name",
+                                       },
+                               },
+                       },
+               ),
+       }
+
+       return trait, environment
+}
diff --git a/pkg/trait/trait_register.go b/pkg/trait/trait_register.go
index c233ca715..90b6a03c0 100644
--- a/pkg/trait/trait_register.go
+++ b/pkg/trait/trait_register.go
@@ -29,6 +29,7 @@ func init() {
        AddToTraits(newDeployerTrait)
        AddToTraits(newDeploymentTrait)
        AddToTraits(newEnvironmentTrait)
+       AddToTraits(newGatewayTrait)
        AddToTraits(newGCTrait)
        AddToTraits(newGitTrait)
        AddToTraits(newGitOpsTrait)
diff --git a/script/Makefile b/script/Makefile
index 2f577dae5..2a0ad5006 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -335,6 +335,12 @@ test-kafka:
 test-telemetry:
        go test -timeout 30m -v ./e2e/telemetry -tags=integration $(GOTESTFMT)
 
+#
+# Gateway tests that require the configuration of a gateway (Envoy)
+#
+test-gateway:
+       go test -timeout 10m -v ./e2e/gateway -tags=integration $(GOTESTFMT)
+
 #
 # Quarkus native test (requires certain CPU and memory conditions)
 #

Reply via email to