You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2026/03/23 08:48:57 UTC

(camel-k) branch main updated: feat(trait): gateway

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 <pa...@gmail.com>
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)
 #