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

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

commit 9a30911d41f02688f153ea9f894ab05518591dd4
Author: Keerthan <[email protected]>
AuthorDate: Wed Sep 16 00:04:17 2026 +0530

    Fix #5857: Add cert-manager auto-discovery to the Ingress trait
---
 .github/workflows/cert-manager.yml                 |  91 ++++++++
 docs/modules/ROOT/partials/apis/camel-k-crds.adoc  |  26 +++
 docs/modules/traits/pages/ingress.adoc             |  17 ++
 e2e/cert-manager/certmanager_test.go               |  85 +++++++
 e2e/cert-manager/files/PlatformHttpServer.java     |  26 +++
 .../cert-manager/setup/cluster-issuer.yaml         |  29 +--
 .../cert-manager/setup/setup.sh                    |  36 ++-
 helm/camel-k/crds/camel-k-crds.yaml                | 136 +++++++++++
 helm/camel-k/templates/rbacs-descoped.yaml         |  32 +++
 helm/camel-k/templates/rbacs-namespaced.yaml       |  31 +++
 pkg/apis/camel/v1/trait/ingress.go                 |  11 +
 pkg/apis/camel/v1/trait/zz_generated.deepcopy.go   |   5 +
 pkg/internal/client.go                             |  20 ++
 .../camel.apache.org_integrationplatforms.yaml     |  34 +++
 .../camel.apache.org_integrationprofiles.yaml      |  34 +++
 .../config/crd/bases/camel.apache.org_pipes.yaml   |  34 +++
 .../config/rbac/descoped/kustomization.yaml        |   2 +
 ...operator-cluster-role-binding-certmanager.yaml} |  36 ++-
 .../operator-cluster-role-certmanager.yaml}        |  39 ++--
 .../config/rbac/namespaced/kustomization.yaml      |   2 +
 ...yaml => operator-role-binding-certmanager.yaml} |  36 ++-
 ...ization.yaml => operator-role-certmanager.yaml} |  39 ++--
 pkg/trait/ingress.go                               | 113 ++++++++-
 pkg/trait/ingress_test.go                          | 142 ++++++++++++
 pkg/util/certmanager/enabled.go                    | 163 +++++++++++++
 pkg/util/certmanager/enabled_test.go               | 252 +++++++++++++++++++++
 script/Makefile                                    |   6 +
 27 files changed, 1336 insertions(+), 141 deletions(-)

diff --git a/.github/workflows/cert-manager.yml 
b/.github/workflows/cert-manager.yml
new file mode 100644
index 000000000..e83e1d46e
--- /dev/null
+++ b/.github/workflows/cert-manager.yml
@@ -0,0 +1,91 @@
+# ---------------------------------------------------------------------------
+# 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: cert-manager
+
+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@v7
+      with:
+        persist-credentials: false
+        submodules: recursive
+
+    - name: Infra setting
+      uses: ./.github/actions/infra-setting
+
+    - name: Install cert-manager
+      shell: bash
+      run: |
+        ./e2e/cert-manager/setup/setup.sh
+
+    - name: Create operator namespace
+      shell: bash
+      run: |
+        kubectl create ns camel-k
+
+    - name: Registry setting
+      uses: ./.github/actions/registry-setting
+
+    - name: Install operator
+      shell: bash
+      run: |
+        make install-k8s-global
+
+    - name: Run test
+      shell: bash
+      run: |
+        DO_TEST_PREBUILD=false GOTESTFMT="-json 2>&1 | gotestfmt" make 
test-cert-manager
diff --git a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc 
b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
index 04c41b894..9509b4a77 100644
--- a/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
+++ b/docs/modules/ROOT/partials/apis/camel-k-crds.adoc
@@ -7872,6 +7872,32 @@ string
 
 To configure tls secret name
 
+|`tlsCertManagerAuto` +
+bool
+|
+
+
+To automatically discover and configure a cert-manager Issuer or ClusterIssuer
+for TLS certificate issuance, in place of manually providing a secret.
+
+|`tlsIssuerName` +
+string
+|
+
+
+The name of the cert-manager Issuer or ClusterIssuer to use for automatic
+TLS certificate issuance. If set, cert-manager availability and the issuer's
+existence are verified, bypassing auto-discovery.
+
+|`tlsIssuerKind` +
+string
+|
+
+
+The kind of the cert-manager issuer to use: `Issuer` (namespaced) or
+`ClusterIssuer` (cluster-scoped). Defaults to `ClusterIssuer` when
+tlsIssuerName is set without a kind.
+
 
 |===
 
diff --git a/docs/modules/traits/pages/ingress.adoc 
b/docs/modules/traits/pages/ingress.adoc
index 86ffa8e10..cd4c55891 100755
--- a/docs/modules/traits/pages/ingress.adoc
+++ b/docs/modules/traits/pages/ingress.adoc
@@ -72,6 +72,23 @@ One of `Exact`, `Prefix`, `ImplementationSpecific` (default 
to `Prefix`).
 | string
 | To configure tls secret name
 
+| ingress.tlsCertManagerAuto
+| bool
+| To automatically discover and configure a cert-manager Issuer or 
ClusterIssuer
+for TLS certificate issuance, in place of manually providing a secret.
+
+| ingress.tlsIssuerName
+| string
+| The name of the cert-manager Issuer or ClusterIssuer to use for automatic
+TLS certificate issuance. If set, cert-manager availability and the issuer's
+existence are verified, bypassing auto-discovery.
+
+| ingress.tlsIssuerKind
+| string
+| The kind of the cert-manager issuer to use: `Issuer` (namespaced) or
+`ClusterIssuer` (cluster-scoped). Defaults to `ClusterIssuer` when
+tlsIssuerName is set without a kind.
+
 |===
 
 NOTE: the variable names are "snake case" if you're using in `kamel` CLI, for 
example `trait.myParam` has to be translated as `-t trait.my-param`
diff --git a/e2e/cert-manager/certmanager_test.go 
b/e2e/cert-manager/certmanager_test.go
new file mode 100644
index 000000000..7a1eed7e6
--- /dev/null
+++ b/e2e/cert-manager/certmanager_test.go
@@ -0,0 +1,85 @@
+//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 certmanager
+
+import (
+       "context"
+       "crypto/x509"
+       "encoding/pem"
+       "testing"
+       "time"
+
+       . "github.com/apache/camel-k/v2/e2e/support"
+       v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/v2/pkg/util/certmanager"
+       . "github.com/onsi/gomega"
+       corev1 "k8s.io/api/core/v1"
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// TestIngressCertManagerAutoDiscovery verifies that the Ingress trait, when
+// tls-cert-manager-auto is enabled, discovers the self-signed ClusterIssuer
+// installed by setup/setup.sh, annotates the generated Ingress accordingly,
+// and that cert-manager actually issues a certificate into the derived
+// "<service>-tls" secret for the configured host.
+func TestIngressCertManagerAutoDiscovery(t *testing.T) {
+       t.Parallel()
+       WithNewTestNamespace(t, func(ctx context.Context, g *WithT, ns string) {
+               host := "cert-manager-it.example.com"
+               integrationName := "platform-http-server"
+
+               g.Expect(KamelRun(t, ctx, ns, "files/PlatformHttpServer.java",
+                       "-t", "ingress.enabled=true",
+                       "-t", "ingress.host="+host,
+                       "-t", "ingress.tls-cert-manager-auto=true",
+               ).Execute()).To(Succeed())
+
+               g.Eventually(IntegrationConditionStatus(t, ctx, ns, 
integrationName,
+                       v1.IntegrationConditionReady), 
TestTimeoutMedium).Should(Equal(corev1.ConditionTrue))
+
+               // The Ingress trait should have auto-discovered the 
ClusterIssuer installed
+               // by setup.sh and annotated the Ingress accordingly.
+               g.Eventually(func() (string, error) {
+                       ingress, err := 
TestClient(t).NetworkingV1().Ingresses(ns).Get(ctx, integrationName, 
metav1.GetOptions{})
+                       if err != nil {
+                               return "", err
+                       }
+
+                       return 
ingress.Annotations[certmanager.AnnotationClusterIssuer], nil
+               }, TestTimeoutShort).Should(Equal("selfsigned-cluster-issuer"))
+
+               // cert-manager should pick up the annotation and populate the 
derived secret.
+               g.Eventually(SecretByName(t, ctx, ns, integrationName+"-tls"), 
TestTimeoutMedium).ShouldNot(BeNil())
+
+               certSecret := SecretByName(t, ctx, ns, integrationName+"-tls")()
+               g.Expect(certSecret.Data).To(HaveKey("tls.crt"))
+               g.Expect(certSecret.Data).To(HaveKey("tls.key"))
+
+               block, _ := pem.Decode(certSecret.Data["tls.crt"])
+               g.Expect(block).NotTo(BeNil())
+               cert, err := x509.ParseCertificate(block.Bytes)
+               g.Expect(err).NotTo(HaveOccurred())
+               g.Expect(cert.DNSNames).To(ContainElement(host))
+               g.Expect(time.Now()).To(BeTemporally("<", cert.NotAfter))
+       })
+}
diff --git a/e2e/cert-manager/files/PlatformHttpServer.java 
b/e2e/cert-manager/files/PlatformHttpServer.java
new file mode 100644
index 000000000..31d8d19f3
--- /dev/null
+++ b/e2e/cert-manager/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/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/e2e/cert-manager/setup/cluster-issuer.yaml
similarity index 58%
copy from pkg/resources/config/rbac/namespaced/kustomization.yaml
copy to e2e/cert-manager/setup/cluster-issuer.yaml
index 1859bd544..ae17ef94b 100644
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ b/e2e/cert-manager/setup/cluster-issuer.yaml
@@ -15,26 +15,9 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-#
-# rbac resources applicable for all kubernetes platforms - namespaced operator
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- operator-role-events.yaml
-- operator-role.yaml
-- operator-role-keda.yaml
-- operator-role-knative.yaml
-- operator-role-leases.yaml
-- operator-role-openshift.yaml
-- operator-role-podmonitors.yaml
-- operator-role-strimzi.yaml
-- operator-role-binding.yaml
-- operator-role-binding-events.yaml
-- operator-role-binding-keda.yaml
-- operator-role-binding-knative.yaml
-- operator-role-binding-leases.yaml
-- operator-role-binding-openshift.yaml
-- operator-role-binding-podmonitors.yaml
-- operator-role-binding-strimzi.yaml
+apiVersion: cert-manager.io/v1
+kind: ClusterIssuer
+metadata:
+  name: selfsigned-cluster-issuer
+spec:
+  selfSigned: {}
diff --git a/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/e2e/cert-manager/setup/setup.sh
old mode 100644
new mode 100755
similarity index 55%
copy from pkg/resources/config/rbac/namespaced/kustomization.yaml
copy to e2e/cert-manager/setup/setup.sh
index 1859bd544..d7ab4caa9
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ b/e2e/cert-manager/setup/setup.sh
@@ -1,3 +1,5 @@
+#!/bin/bash
+
 # ---------------------------------------------------------------------------
 # Licensed to the Apache Software Foundation (ASF) under one or more
 # contributor license agreements.  See the NOTICE file distributed with
@@ -6,7 +8,7 @@
 # (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
+#     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,
@@ -15,26 +17,14 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-#
-# rbac resources applicable for all kubernetes platforms - namespaced operator
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
+CERT_MANAGER_VERSION="v1.21.2"
+TIMEOUT="150s"
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+kubectl apply -f 
https://github.com/cert-manager/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml
+kubectl wait --for=condition=available deployment/cert-manager -n cert-manager 
--timeout=$TIMEOUT
+kubectl wait --for=condition=available deployment/cert-manager-webhook -n 
cert-manager --timeout=$TIMEOUT
+kubectl wait --for=condition=available deployment/cert-manager-cainjector -n 
cert-manager --timeout=$TIMEOUT
 
-resources:
-- operator-role-events.yaml
-- operator-role.yaml
-- operator-role-keda.yaml
-- operator-role-knative.yaml
-- operator-role-leases.yaml
-- operator-role-openshift.yaml
-- operator-role-podmonitors.yaml
-- operator-role-strimzi.yaml
-- operator-role-binding.yaml
-- operator-role-binding-events.yaml
-- operator-role-binding-keda.yaml
-- operator-role-binding-knative.yaml
-- operator-role-binding-leases.yaml
-- operator-role-binding-openshift.yaml
-- operator-role-binding-podmonitors.yaml
-- operator-role-binding-strimzi.yaml
+# Install a self-signed ClusterIssuer for test purposes
+kubectl apply -f $SCRIPT_DIR/cluster-issuer.yaml
diff --git a/helm/camel-k/crds/camel-k-crds.yaml 
b/helm/camel-k/crds/camel-k-crds.yaml
index 2f972f821..97974af6d 100644
--- a/helm/camel-k/crds/camel-k-crds.yaml
+++ b/helm/camel-k/crds/camel-k-crds.yaml
@@ -4693,11 +4693,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -7238,11 +7255,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -9744,11 +9778,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -12238,11 +12289,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -21794,11 +21862,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -24176,11 +24261,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -35095,11 +35197,28 @@ spec:
                             items:
                               type: string
                             type: array
+                          tlsCertManagerAuto:
+                            description: |-
+                              To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                              for TLS certificate issuance, in place of 
manually providing a secret.
+                            type: boolean
                           tlsHosts:
                             description: To configure tls hosts
                             items:
                               type: string
                             type: array
+                          tlsIssuerKind:
+                            description: |-
+                              The kind of the cert-manager issuer to use: 
`Issuer` (namespaced) or
+                              `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                              tlsIssuerName is set without a kind.
+                            type: string
+                          tlsIssuerName:
+                            description: |-
+                              The name of the cert-manager Issuer or 
ClusterIssuer to use for automatic
+                              TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                              existence are verified, bypassing auto-discovery.
+                            type: string
                           tlsSecretName:
                             description: To configure tls secret name
                             type: string
@@ -37399,11 +37518,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
diff --git a/helm/camel-k/templates/rbacs-descoped.yaml 
b/helm/camel-k/templates/rbacs-descoped.yaml
index 8cdd8aaf6..1c515a1ce 100644
--- a/helm/camel-k/templates/rbacs-descoped.yaml
+++ b/helm/camel-k/templates/rbacs-descoped.yaml
@@ -217,6 +217,23 @@ rules:
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
+metadata:
+  labels:
+    app: camel-k
+  name: {{ include "camel-k.fullname" . }}-operator-certmanager
+rules:
+- apiGroups:
+  - cert-manager.io
+  resources:
+  - issuers
+  - clusterissuers
+  verbs:
+  - get
+  - list
+  - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
 metadata:
   labels:
     app: camel-k
@@ -474,6 +491,21 @@ subjects:
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
+metadata:
+  labels:
+    app: camel-k
+  name: {{ include "camel-k.fullname" . }}-operator-certmanager
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: {{ include "camel-k.fullname" . }}-operator-certmanager
+subjects:
+- kind: ServiceAccount
+  name: {{ include "camel-k.fullname" . }}-operator
+  namespace: '{{ .Release.Namespace }}'
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
 metadata:
   labels:
     app: camel-k
diff --git a/helm/camel-k/templates/rbacs-namespaced.yaml 
b/helm/camel-k/templates/rbacs-namespaced.yaml
index 530539539..d43d5db1f 100644
--- a/helm/camel-k/templates/rbacs-namespaced.yaml
+++ b/helm/camel-k/templates/rbacs-namespaced.yaml
@@ -211,6 +211,23 @@ rules:
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: Role
+metadata:
+  labels:
+    app: camel-k
+  name: {{ include "camel-k.fullname" . }}-operator-certmanager
+rules:
+- apiGroups:
+  - cert-manager.io
+  resources:
+  - issuers
+  - clusterissuers
+  verbs:
+  - get
+  - list
+  - watch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
 metadata:
   labels:
     app: camel-k
@@ -467,6 +484,20 @@ subjects:
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: RoleBinding
+metadata:
+  labels:
+    app: camel-k
+  name: {{ include "camel-k.fullname" . }}-operator-certmanager
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: {{ include "camel-k.fullname" . }}-operator-certmanager
+subjects:
+- kind: ServiceAccount
+  name: {{ include "camel-k.fullname" . }}-operator
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
 metadata:
   labels:
     app: camel-k
diff --git a/pkg/apis/camel/v1/trait/ingress.go 
b/pkg/apis/camel/v1/trait/ingress.go
index 0768998ca..364a05013 100644
--- a/pkg/apis/camel/v1/trait/ingress.go
+++ b/pkg/apis/camel/v1/trait/ingress.go
@@ -55,4 +55,15 @@ type IngressTrait struct {
        TLSHosts []string `json:"tlsHosts,omitempty" property:"tls-hosts"`
        // To configure tls secret name
        TLSSecretName string `json:"tlsSecretName,omitempty" 
property:"tls-secret-name"`
+       // To automatically discover and configure a cert-manager Issuer or 
ClusterIssuer
+       // for TLS certificate issuance, in place of manually providing a 
secret.
+       TLSCertManagerAuto *bool `json:"tlsCertManagerAuto,omitempty" 
property:"tls-cert-manager-auto"`
+       // The name of the cert-manager Issuer or ClusterIssuer to use for 
automatic
+       // TLS certificate issuance. If set, cert-manager availability and the 
issuer's
+       // existence are verified, bypassing auto-discovery.
+       TLSIssuerName string `json:"tlsIssuerName,omitempty" 
property:"tls-issuer-name"`
+       // The kind of the cert-manager issuer to use: `Issuer` (namespaced) or
+       // `ClusterIssuer` (cluster-scoped). Defaults to `ClusterIssuer` when
+       // tlsIssuerName is set without a kind.
+       TLSIssuerKind string `json:"tlsIssuerKind,omitempty" 
property:"tls-issuer-kind"`
 }
diff --git a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go 
b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
index 03e7c8716..190cbfade 100644
--- a/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1/trait/zz_generated.deepcopy.go
@@ -544,6 +544,11 @@ func (in *IngressTrait) DeepCopyInto(out *IngressTrait) {
                *out = make([]string, len(*in))
                copy(*out, *in)
        }
+       if in.TLSCertManagerAuto != nil {
+               in, out := &in.TLSCertManagerAuto, &out.TLSCertManagerAuto
+               *out = new(bool)
+               **out = **in
+       }
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, 
creating a new IngressTrait.
diff --git a/pkg/internal/client.go b/pkg/internal/client.go
index 4366a2ef8..2c15d3892 100644
--- a/pkg/internal/client.go
+++ b/pkg/internal/client.go
@@ -151,6 +151,7 @@ type FakeClient struct {
        enabledOpenshift       bool
        enabledKnativeServing  bool
        enabledKnativeEventing bool
+       enabledCertManager     bool
 }
 
 func (c *FakeClient) Intercept(intercept *interceptor.Funcs) {
@@ -202,6 +203,10 @@ func (c *FakeClient) EnableOpenshiftDiscovery() {
        c.enabledOpenshift = true
 }
 
+func (c *FakeClient) EnableCertManagerDiscovery() {
+       c.enabledCertManager = true
+}
+
 func (c *FakeClient) DisableKnativeServing() {
        c.enabledKnativeServing = false
 }
@@ -225,6 +230,7 @@ func (c *FakeClient) Discovery() 
discovery.DiscoveryInterfaces {
                enabledOpenshift:       c.enabledOpenshift,
                enabledKnativeServing:  c.enabledKnativeServing,
                enabledKnativeEventing: c.enabledKnativeEventing,
+               enabledCertManager:     c.enabledCertManager,
        }
 }
 
@@ -276,6 +282,7 @@ type FakeDiscovery struct {
        enabledOpenshift       bool
        enabledKnativeServing  bool
        enabledKnativeEventing bool
+       enabledCertManager     bool
 }
 
 func (f *FakeDiscovery) ServerResourcesForGroupVersion(groupVersion string) 
(*metav1.APIResourceList, error) {
@@ -292,6 +299,19 @@ func (f *FakeDiscovery) 
ServerResourcesForGroupVersion(groupVersion string) (*me
                }
        }
 
+       // used to verify if cert-manager is installed
+       if groupVersion == "cert-manager.io/v1" {
+               if f.enabledCertManager {
+                       return &metav1.APIResourceList{
+                               GroupVersion: "cert-manager.io/v1",
+                       }, nil
+               }
+
+               return nil, k8serrors.NewNotFound(schema.GroupResource{
+                       Group: "cert-manager.io",
+               }, "")
+       }
+
        // used to verify if Knative Serving is installed
        if f.enabledKnativeServing {
                if groupVersion == "serving.knative.dev/v1" && 
!util.StringSliceExists(f.disabledGroups, groupVersion) {
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 1b96264f9..cd875bf7e 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationplatforms.yaml
@@ -1385,11 +1385,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -3930,11 +3947,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
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 7a893b272..af5c8dd2f 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_integrationprofiles.yaml
@@ -1310,11 +1310,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
@@ -3804,11 +3821,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
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 7cc12f6b9..df85d293c 100644
--- a/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
+++ b/pkg/resources/config/crd/bases/camel.apache.org_pipes.yaml
@@ -8418,11 +8418,28 @@ spec:
                             items:
                               type: string
                             type: array
+                          tlsCertManagerAuto:
+                            description: |-
+                              To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                              for TLS certificate issuance, in place of 
manually providing a secret.
+                            type: boolean
                           tlsHosts:
                             description: To configure tls hosts
                             items:
                               type: string
                             type: array
+                          tlsIssuerKind:
+                            description: |-
+                              The kind of the cert-manager issuer to use: 
`Issuer` (namespaced) or
+                              `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                              tlsIssuerName is set without a kind.
+                            type: string
+                          tlsIssuerName:
+                            description: |-
+                              The name of the cert-manager Issuer or 
ClusterIssuer to use for automatic
+                              TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                              existence are verified, bypassing auto-discovery.
+                            type: string
                           tlsSecretName:
                             description: To configure tls secret name
                             type: string
@@ -10722,11 +10739,28 @@ spec:
                         items:
                           type: string
                         type: array
+                      tlsCertManagerAuto:
+                        description: |-
+                          To automatically discover and configure a 
cert-manager Issuer or ClusterIssuer
+                          for TLS certificate issuance, in place of manually 
providing a secret.
+                        type: boolean
                       tlsHosts:
                         description: To configure tls hosts
                         items:
                           type: string
                         type: array
+                      tlsIssuerKind:
+                        description: |-
+                          The kind of the cert-manager issuer to use: `Issuer` 
(namespaced) or
+                          `ClusterIssuer` (cluster-scoped). Defaults to 
`ClusterIssuer` when
+                          tlsIssuerName is set without a kind.
+                        type: string
+                      tlsIssuerName:
+                        description: |-
+                          The name of the cert-manager Issuer or ClusterIssuer 
to use for automatic
+                          TLS certificate issuance. If set, cert-manager 
availability and the issuer's
+                          existence are verified, bypassing auto-discovery.
+                        type: string
                       tlsSecretName:
                         description: To configure tls secret name
                         type: string
diff --git a/pkg/resources/config/rbac/descoped/kustomization.yaml 
b/pkg/resources/config/rbac/descoped/kustomization.yaml
index 68f35074a..54c7ebb7e 100644
--- a/pkg/resources/config/rbac/descoped/kustomization.yaml
+++ b/pkg/resources/config/rbac/descoped/kustomization.yaml
@@ -22,6 +22,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
 resources:
+- operator-cluster-role-certmanager.yaml
 - operator-cluster-role-events.yaml
 - operator-cluster-role.yaml
 - operator-cluster-role-keda.yaml
@@ -30,6 +31,7 @@ resources:
 - operator-cluster-role-openshift.yaml
 - operator-cluster-role-podmonitors.yaml
 - operator-cluster-role-strimzi.yaml
+- operator-cluster-role-binding-certmanager.yaml
 - operator-cluster-role-binding-events.yaml
 - operator-cluster-role-binding-keda.yaml
 - operator-cluster-role-binding-knative.yaml
diff --git a/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/pkg/resources/config/rbac/descoped/operator-cluster-role-binding-certmanager.yaml
similarity index 58%
copy from pkg/resources/config/rbac/namespaced/kustomization.yaml
copy to 
pkg/resources/config/rbac/descoped/operator-cluster-role-binding-certmanager.yaml
index 1859bd544..0b444f5c4 100644
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ 
b/pkg/resources/config/rbac/descoped/operator-cluster-role-binding-certmanager.yaml
@@ -15,26 +15,16 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-#
-# rbac resources applicable for all kubernetes platforms - namespaced operator
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- operator-role-events.yaml
-- operator-role.yaml
-- operator-role-keda.yaml
-- operator-role-knative.yaml
-- operator-role-leases.yaml
-- operator-role-openshift.yaml
-- operator-role-podmonitors.yaml
-- operator-role-strimzi.yaml
-- operator-role-binding.yaml
-- operator-role-binding-events.yaml
-- operator-role-binding-keda.yaml
-- operator-role-binding-knative.yaml
-- operator-role-binding-leases.yaml
-- operator-role-binding-openshift.yaml
-- operator-role-binding-podmonitors.yaml
-- operator-role-binding-strimzi.yaml
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k-operator-certmanager
+  labels:
+    app: "camel-k"
+subjects:
+- kind: ServiceAccount
+  name: camel-k-operator
+roleRef:
+  kind: ClusterRole
+  name: camel-k-operator-certmanager
+  apiGroup: rbac.authorization.k8s.io
diff --git a/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/pkg/resources/config/rbac/descoped/operator-cluster-role-certmanager.yaml
similarity index 58%
copy from pkg/resources/config/rbac/namespaced/kustomization.yaml
copy to 
pkg/resources/config/rbac/descoped/operator-cluster-role-certmanager.yaml
index 1859bd544..60fc584e3 100644
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ b/pkg/resources/config/rbac/descoped/operator-cluster-role-certmanager.yaml
@@ -15,26 +15,19 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-#
-# rbac resources applicable for all kubernetes platforms - namespaced operator
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- operator-role-events.yaml
-- operator-role.yaml
-- operator-role-keda.yaml
-- operator-role-knative.yaml
-- operator-role-leases.yaml
-- operator-role-openshift.yaml
-- operator-role-podmonitors.yaml
-- operator-role-strimzi.yaml
-- operator-role-binding.yaml
-- operator-role-binding-events.yaml
-- operator-role-binding-keda.yaml
-- operator-role-binding-knative.yaml
-- operator-role-binding-leases.yaml
-- operator-role-binding-openshift.yaml
-- operator-role-binding-podmonitors.yaml
-- operator-role-binding-strimzi.yaml
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k-operator-certmanager
+  labels:
+    app: "camel-k"
+rules:
+- apiGroups:
+  - "cert-manager.io"
+  resources:
+  - issuers
+  - clusterissuers
+  verbs:
+  - get
+  - list
+  - watch
diff --git a/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/pkg/resources/config/rbac/namespaced/kustomization.yaml
index 1859bd544..e50bc5c5a 100644
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ b/pkg/resources/config/rbac/namespaced/kustomization.yaml
@@ -22,6 +22,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 
 resources:
+- operator-role-certmanager.yaml
 - operator-role-events.yaml
 - operator-role.yaml
 - operator-role-keda.yaml
@@ -31,6 +32,7 @@ resources:
 - operator-role-podmonitors.yaml
 - operator-role-strimzi.yaml
 - operator-role-binding.yaml
+- operator-role-binding-certmanager.yaml
 - operator-role-binding-events.yaml
 - operator-role-binding-keda.yaml
 - operator-role-binding-knative.yaml
diff --git a/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/pkg/resources/config/rbac/namespaced/operator-role-binding-certmanager.yaml
similarity index 58%
copy from pkg/resources/config/rbac/namespaced/kustomization.yaml
copy to 
pkg/resources/config/rbac/namespaced/operator-role-binding-certmanager.yaml
index 1859bd544..0ae88e3c8 100644
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ 
b/pkg/resources/config/rbac/namespaced/operator-role-binding-certmanager.yaml
@@ -15,26 +15,16 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-#
-# rbac resources applicable for all kubernetes platforms - namespaced operator
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- operator-role-events.yaml
-- operator-role.yaml
-- operator-role-keda.yaml
-- operator-role-knative.yaml
-- operator-role-leases.yaml
-- operator-role-openshift.yaml
-- operator-role-podmonitors.yaml
-- operator-role-strimzi.yaml
-- operator-role-binding.yaml
-- operator-role-binding-events.yaml
-- operator-role-binding-keda.yaml
-- operator-role-binding-knative.yaml
-- operator-role-binding-leases.yaml
-- operator-role-binding-openshift.yaml
-- operator-role-binding-podmonitors.yaml
-- operator-role-binding-strimzi.yaml
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k-operator-certmanager
+  labels:
+    app: "camel-k"
+subjects:
+- kind: ServiceAccount
+  name: camel-k-operator
+roleRef:
+  kind: Role
+  name: camel-k-operator-certmanager
+  apiGroup: rbac.authorization.k8s.io
diff --git a/pkg/resources/config/rbac/namespaced/kustomization.yaml 
b/pkg/resources/config/rbac/namespaced/operator-role-certmanager.yaml
similarity index 58%
copy from pkg/resources/config/rbac/namespaced/kustomization.yaml
copy to pkg/resources/config/rbac/namespaced/operator-role-certmanager.yaml
index 1859bd544..178420a8a 100644
--- a/pkg/resources/config/rbac/namespaced/kustomization.yaml
+++ b/pkg/resources/config/rbac/namespaced/operator-role-certmanager.yaml
@@ -15,26 +15,19 @@
 # limitations under the License.
 # ---------------------------------------------------------------------------
 
-#
-# rbac resources applicable for all kubernetes platforms - namespaced operator
-#
-apiVersion: kustomize.config.k8s.io/v1beta1
-kind: Kustomization
-
-resources:
-- operator-role-events.yaml
-- operator-role.yaml
-- operator-role-keda.yaml
-- operator-role-knative.yaml
-- operator-role-leases.yaml
-- operator-role-openshift.yaml
-- operator-role-podmonitors.yaml
-- operator-role-strimzi.yaml
-- operator-role-binding.yaml
-- operator-role-binding-events.yaml
-- operator-role-binding-keda.yaml
-- operator-role-binding-knative.yaml
-- operator-role-binding-leases.yaml
-- operator-role-binding-openshift.yaml
-- operator-role-binding-podmonitors.yaml
-- operator-role-binding-strimzi.yaml
+kind: Role
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+  name: camel-k-operator-certmanager
+  labels:
+    app: "camel-k"
+rules:
+- apiGroups:
+  - "cert-manager.io"
+  resources:
+  - issuers
+  - clusterissuers
+  verbs:
+  - get
+  - list
+  - watch
diff --git a/pkg/trait/ingress.go b/pkg/trait/ingress.go
index 943b7b1e7..5ef9247dd 100644
--- a/pkg/trait/ingress.go
+++ b/pkg/trait/ingress.go
@@ -23,6 +23,7 @@ import (
 
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
        traitv1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1/trait"
+       "github.com/apache/camel-k/v2/pkg/util/certmanager"
        corev1 "k8s.io/api/core/v1"
        networkingv1 "k8s.io/api/networking/v1"
        "k8s.io/utils/ptr"
@@ -124,11 +125,38 @@ func (t *ingressTrait) Apply(e *Environment) error {
                ingress.Spec.IngressClassName = &t.IngressClassName
        }
 
-       if len(t.TLSHosts) > 0 && t.TLSSecretName != "" {
+       tlsHosts := t.TLSHosts
+       secretName := t.TLSSecretName
+
+       // The cert-manager path only activates when the user has not manually 
provided a
+       // secret name. It may fall back to t.Host so that auto-discovery also 
works for the
+       // common single-host case, without changing the pre-existing manual 
TLS behavior
+       // (which requires TLSHosts to be set explicitly and never considers 
t.Host).
+       if secretName == "" {
+               if len(tlsHosts) == 0 && t.Host != "" {
+                       tlsHosts = []string{t.Host}
+               }
+
+               if len(tlsHosts) > 0 {
+                       annotationKey, issuerName, err := 
t.resolveCertManagerIssuer(e)
+                       if err != nil {
+                               return err
+                       }
+                       if issuerName != "" {
+                               if ingress.Annotations == nil {
+                                       ingress.Annotations = 
map[string]string{}
+                               }
+                               ingress.Annotations[annotationKey] = issuerName
+                               secretName = service.Name + "-tls"
+                       }
+               }
+       }
+
+       if len(tlsHosts) > 0 && secretName != "" {
                ingress.Spec.TLS = []networkingv1.IngressTLS{
                        {
-                               Hosts:      t.TLSHosts,
-                               SecretName: t.TLSSecretName,
+                               Hosts:      tlsHosts,
+                               SecretName: secretName,
                        },
                }
        }
@@ -147,6 +175,85 @@ func (t *ingressTrait) Apply(e *Environment) error {
        return nil
 }
 
+// resolveCertManagerIssuer determines which cert-manager Issuer or 
ClusterIssuer
+// annotation to apply to the Ingress, if any. It returns an empty issuerName 
when
+// no annotation should be applied (cert-manager auto-discovery is disabled, 
cert-manager
+// is not installed, or no issuer is found). A forced TLSIssuerName is 
verified to exist
+// and returns an error if it does not; auto-discovery degrades to a no-op 
instead.
+func (t *ingressTrait) resolveCertManagerIssuer(e *Environment) 
(annotationKey, issuerName string, err error) {
+       namespace := e.Integration.Namespace
+
+       if t.TLSIssuerName != "" {
+               installed, err := certmanager.IsInstalled(e.Client)
+               if err != nil {
+                       return "", "", err
+               }
+               if !installed {
+                       return "", "", fmt.Errorf("cert-manager is not 
installed but tlsIssuerName %q was set", t.TLSIssuerName)
+               }
+
+               kind := t.TLSIssuerKind
+               if kind == "" {
+                       kind = "ClusterIssuer"
+               }
+
+               switch kind {
+               case "Issuer":
+                       exists, err := certmanager.GetIssuer(e.Ctx, e.Client, 
namespace, t.TLSIssuerName)
+                       if err != nil {
+                               return "", "", err
+                       }
+                       if !exists {
+                               return "", "", fmt.Errorf("issuer %q not found 
in namespace %q", t.TLSIssuerName, namespace)
+                       }
+
+                       return certmanager.AnnotationIssuer, t.TLSIssuerName, 
nil
+               case "ClusterIssuer":
+                       exists, err := certmanager.GetClusterIssuer(e.Ctx, 
e.Client, t.TLSIssuerName)
+                       if err != nil {
+                               return "", "", err
+                       }
+                       if !exists {
+                               return "", "", fmt.Errorf("clusterissuer %q not 
found", t.TLSIssuerName)
+                       }
+
+                       return certmanager.AnnotationClusterIssuer, 
t.TLSIssuerName, nil
+               default:
+                       return "", "", fmt.Errorf("invalid tlsIssuerKind %q: 
must be %q or %q", kind, "Issuer", "ClusterIssuer")
+               }
+       }
+
+       if !ptr.Deref(t.TLSCertManagerAuto, false) {
+               return "", "", nil
+       }
+
+       installed, err := certmanager.IsInstalled(e.Client)
+       if err != nil {
+               return "", "", err
+       }
+       if !installed {
+               return "", "", nil
+       }
+
+       clusterIssuers, err := certmanager.ListClusterIssuers(e.Ctx, e.Client)
+       if err != nil {
+               return "", "", err
+       }
+       if len(clusterIssuers) > 0 {
+               return certmanager.AnnotationClusterIssuer, clusterIssuers[0], 
nil
+       }
+
+       issuers, err := certmanager.ListIssuers(e.Ctx, e.Client, namespace)
+       if err != nil {
+               return "", "", err
+       }
+       if len(issuers) > 0 {
+               return certmanager.AnnotationIssuer, issuers[0], nil
+       }
+
+       return "", "", nil
+}
+
 func (t *ingressTrait) getPaths(service *corev1.Service) 
[]networkingv1.HTTPIngressPath {
        createIngressPath := func(path string) networkingv1.HTTPIngressPath {
                return networkingv1.HTTPIngressPath{
diff --git a/pkg/trait/ingress_test.go b/pkg/trait/ingress_test.go
index 763cfcbd9..56731b2c2 100644
--- a/pkg/trait/ingress_test.go
+++ b/pkg/trait/ingress_test.go
@@ -18,6 +18,7 @@ limitations under the License.
 package trait
 
 import (
+       "context"
        "testing"
 
        "github.com/stretchr/testify/assert"
@@ -26,10 +27,13 @@ import (
        corev1 "k8s.io/api/core/v1"
        networkingv1 "k8s.io/api/networking/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
        "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/utils/ptr"
 
        v1 "github.com/apache/camel-k/v2/pkg/apis/camel/v1"
+       "github.com/apache/camel-k/v2/pkg/internal"
+       "github.com/apache/camel-k/v2/pkg/util/certmanager"
        "github.com/apache/camel-k/v2/pkg/util/kubernetes"
 )
 
@@ -335,6 +339,144 @@ func 
TestConfigureTLSWithoutSecretNameIngressTraitWDoesSucceed(t *testing.T) {
        assert.Equal(t, "service-name(hostname) -> service-name(http)", 
conditions[0].Message)
 }
 
+func TestApplyIngressTraitCertManagerAutoNotInstalledDoesNoop(t *testing.T) {
+       ingressTrait, environment := createNominalIngressTest()
+       ingressTrait.TLSCertManagerAuto = ptr.To(true)
+       environment.Ctx = context.Background()
+       fakeClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+       environment.Client = fakeClient
+
+       err = ingressTrait.Apply(environment)
+
+       require.NoError(t, err)
+       environment.Resources.Visit(func(resource runtime.Object) {
+               if ingress, ok := resource.(*networkingv1.Ingress); ok {
+                       assert.Nil(t, ingress.Spec.TLS)
+                       assert.NotContains(t, ingress.Annotations, 
certmanager.AnnotationClusterIssuer)
+                       assert.NotContains(t, ingress.Annotations, 
certmanager.AnnotationIssuer)
+               }
+       })
+}
+
+func TestApplyIngressTraitCertManagerAutoNoIssuerDoesNoop(t *testing.T) {
+       ingressTrait, environment := createNominalIngressTest()
+       ingressTrait.TLSCertManagerAuto = ptr.To(true)
+       environment.Ctx = context.Background()
+       fakeClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+       fakeClient.(*internal.FakeClient).EnableCertManagerDiscovery()
+       environment.Client = fakeClient
+
+       err = ingressTrait.Apply(environment)
+
+       require.NoError(t, err)
+       environment.Resources.Visit(func(resource runtime.Object) {
+               if ingress, ok := resource.(*networkingv1.Ingress); ok {
+                       assert.Nil(t, ingress.Spec.TLS)
+                       assert.NotContains(t, ingress.Annotations, 
certmanager.AnnotationClusterIssuer)
+               }
+       })
+}
+
+func TestApplyIngressTraitCertManagerAutoClusterIssuerFoundDoesSucceed(t 
*testing.T) {
+       ingressTrait, environment := createNominalIngressTest()
+       ingressTrait.TLSCertManagerAuto = ptr.To(true)
+       environment.Ctx = context.Background()
+
+       clusterIssuer := newClusterIssuer("letsencrypt-prod")
+       fakeClient, err := internal.NewFakeClient(clusterIssuer)
+       require.NoError(t, err)
+       fakeClient.(*internal.FakeClient).EnableCertManagerDiscovery()
+       environment.Client = fakeClient
+
+       err = ingressTrait.Apply(environment)
+
+       require.NoError(t, err)
+       environment.Resources.Visit(func(resource runtime.Object) {
+               if ingress, ok := resource.(*networkingv1.Ingress); ok {
+                       assert.Equal(t, "letsencrypt-prod", 
ingress.Annotations[certmanager.AnnotationClusterIssuer])
+                       require.NotNil(t, ingress.Spec.TLS)
+                       assert.Equal(t, []string{"hostname"}, 
ingress.Spec.TLS[0].Hosts)
+                       assert.Equal(t, "service-name-tls", 
ingress.Spec.TLS[0].SecretName)
+               }
+       })
+}
+
+func TestApplyIngressTraitForcedIssuerExistsDoesSucceed(t *testing.T) {
+       ingressTrait, environment := createNominalIngressTest()
+       ingressTrait.TLSIssuerName = "my-issuer"
+       ingressTrait.TLSIssuerKind = "Issuer"
+       environment.Ctx = context.Background()
+       environment.Integration.Namespace = "namespace"
+
+       issuer := newIssuer("my-issuer", "namespace")
+       fakeClient, err := internal.NewFakeClient(issuer)
+       require.NoError(t, err)
+       fakeClient.(*internal.FakeClient).EnableCertManagerDiscovery()
+       environment.Client = fakeClient
+
+       err = ingressTrait.Apply(environment)
+
+       require.NoError(t, err)
+       environment.Resources.Visit(func(resource runtime.Object) {
+               if ingress, ok := resource.(*networkingv1.Ingress); ok {
+                       assert.Equal(t, "my-issuer", 
ingress.Annotations[certmanager.AnnotationIssuer])
+                       require.NotNil(t, ingress.Spec.TLS)
+                       assert.Equal(t, "service-name-tls", 
ingress.Spec.TLS[0].SecretName)
+               }
+       })
+}
+
+func TestApplyIngressTraitForcedIssuerMissingDoesNotSucceed(t *testing.T) {
+       ingressTrait, environment := createNominalIngressTest()
+       ingressTrait.TLSIssuerName = "missing-issuer"
+       environment.Ctx = context.Background()
+
+       fakeClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+       fakeClient.(*internal.FakeClient).EnableCertManagerDiscovery()
+       environment.Client = fakeClient
+
+       err = ingressTrait.Apply(environment)
+
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), "missing-issuer")
+       assert.Contains(t, err.Error(), "not found")
+}
+
+func TestApplyIngressTraitForcedIssuerCertManagerNotInstalledDoesNotSucceed(t 
*testing.T) {
+       ingressTrait, environment := createNominalIngressTest()
+       ingressTrait.TLSIssuerName = "my-issuer"
+       environment.Ctx = context.Background()
+
+       fakeClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+       environment.Client = fakeClient
+
+       err = ingressTrait.Apply(environment)
+
+       require.Error(t, err)
+       assert.Contains(t, err.Error(), "cert-manager is not installed")
+}
+
+func newClusterIssuer(name string) *unstructured.Unstructured {
+       u := &unstructured.Unstructured{}
+       u.SetGroupVersionKind(certmanager.ClusterIssuerGVK)
+       u.SetName(name)
+
+       return u
+}
+
+func newIssuer(name, namespace string) *unstructured.Unstructured {
+       u := &unstructured.Unstructured{}
+       u.SetGroupVersionKind(certmanager.IssuerGVK)
+       u.SetName(name)
+       u.SetNamespace(namespace)
+
+       return u
+}
+
 func createNominalIngressTestWithIngressClassName(ingressClassName string) 
(*ingressTrait, *Environment) {
        trait, environment := createNominalIngressTest()
        trait.IngressClassName = ingressClassName
diff --git a/pkg/util/certmanager/enabled.go b/pkg/util/certmanager/enabled.go
new file mode 100644
index 000000000..627002280
--- /dev/null
+++ b/pkg/util/certmanager/enabled.go
@@ -0,0 +1,163 @@
+/*
+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 certmanager
+
+import (
+       "context"
+       "sort"
+
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/api/meta"
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+       "k8s.io/client-go/kubernetes"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+
+       kubernetesutil "github.com/apache/camel-k/v2/pkg/util/kubernetes"
+)
+
+const (
+       // CertManagerAPIGroup is the API group for cert-manager.
+       CertManagerAPIGroup = "cert-manager.io"
+       // CertManagerAPIVersion is the current API version for cert-manager.
+       CertManagerAPIVersion = "v1"
+
+       // AnnotationClusterIssuer is the Ingress annotation to specify a 
ClusterIssuer.
+       AnnotationClusterIssuer = "cert-manager.io/cluster-issuer"
+       // AnnotationIssuer is the Ingress annotation to specify a namespaced 
Issuer.
+       AnnotationIssuer = "cert-manager.io/issuer"
+)
+
+var (
+       // ClusterIssuerGVK is the GroupVersionKind for cert-manager 
ClusterIssuer.
+       ClusterIssuerGVK = schema.GroupVersionKind{
+               Group:   CertManagerAPIGroup,
+               Version: CertManagerAPIVersion,
+               Kind:    "ClusterIssuer",
+       }
+
+       // IssuerGVK is the GroupVersionKind for cert-manager Issuer.
+       IssuerGVK = schema.GroupVersionKind{
+               Group:   CertManagerAPIGroup,
+               Version: CertManagerAPIVersion,
+               Kind:    "Issuer",
+       }
+)
+
+func isResourceNotFoundError(err error) bool {
+       if err == nil {
+               return false
+       }
+
+       return k8serrors.IsNotFound(err) || meta.IsNoMatchError(err) || 
kubernetesutil.IsUnknownAPIError(err)
+}
+
+// IsInstalled returns true if connected to a cluster with cert-manager 
installed.
+func IsInstalled(c kubernetes.Interface) (bool, error) {
+       _, err := 
c.Discovery().ServerResourcesForGroupVersion(schema.GroupVersion{
+               Group:   CertManagerAPIGroup,
+               Version: CertManagerAPIVersion,
+       }.String())
+       if isResourceNotFoundError(err) {
+               return false, nil
+       } else if err != nil {
+               return false, err
+       }
+
+       return true, nil
+}
+
+// ListClusterIssuers returns all ClusterIssuer names available in the cluster.
+func ListClusterIssuers(ctx context.Context, c ctrl.Reader) ([]string, error) {
+       list := &unstructured.UnstructuredList{}
+       list.SetGroupVersionKind(schema.GroupVersionKind{
+               Group:   CertManagerAPIGroup,
+               Version: CertManagerAPIVersion,
+               Kind:    "ClusterIssuerList",
+       })
+
+       if err := c.List(ctx, list); err != nil {
+               if isResourceNotFoundError(err) {
+                       return nil, nil
+               }
+
+               return nil, err
+       }
+
+       names := make([]string, 0, len(list.Items))
+       for _, item := range list.Items {
+               names = append(names, item.GetName())
+       }
+       sort.Strings(names)
+
+       return names, nil
+}
+
+// GetClusterIssuer checks if a specific ClusterIssuer exists in the cluster.
+func GetClusterIssuer(ctx context.Context, c ctrl.Reader, name string) (bool, 
error) {
+       _, err := kubernetesutil.GetUnstructured(ctx, c, ClusterIssuerGVK, 
name, "")
+       if err != nil {
+               if isResourceNotFoundError(err) {
+                       return false, nil
+               }
+
+               return false, err
+       }
+
+       return true, nil
+}
+
+// ListIssuers returns all Issuer names available in a specific namespace.
+func ListIssuers(ctx context.Context, c ctrl.Reader, namespace string) 
([]string, error) {
+       list := &unstructured.UnstructuredList{}
+       list.SetGroupVersionKind(schema.GroupVersionKind{
+               Group:   CertManagerAPIGroup,
+               Version: CertManagerAPIVersion,
+               Kind:    "IssuerList",
+       })
+
+       if err := c.List(ctx, list, ctrl.InNamespace(namespace)); err != nil {
+               if isResourceNotFoundError(err) {
+                       return nil, nil
+               }
+
+               return nil, err
+       }
+
+       names := make([]string, 0, len(list.Items))
+       for _, item := range list.Items {
+               names = append(names, item.GetName())
+       }
+       sort.Strings(names)
+
+       return names, nil
+}
+
+// GetIssuer checks if a specific namespaced Issuer exists.
+func GetIssuer(ctx context.Context, c ctrl.Reader, namespace, name string) 
(bool, error) {
+       _, err := kubernetesutil.GetUnstructured(ctx, c, IssuerGVK, name, 
namespace)
+       if err != nil {
+               if isResourceNotFoundError(err) {
+                       return false, nil
+               }
+
+               return false, err
+       }
+
+       return true, nil
+}
diff --git a/pkg/util/certmanager/enabled_test.go 
b/pkg/util/certmanager/enabled_test.go
new file mode 100644
index 000000000..65a0c60ef
--- /dev/null
+++ b/pkg/util/certmanager/enabled_test.go
@@ -0,0 +1,252 @@
+/*
+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 certmanager
+
+import (
+       "context"
+       "errors"
+       "fmt"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+       k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/api/meta"
+       "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+       "k8s.io/apimachinery/pkg/runtime/schema"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
+
+       "github.com/apache/camel-k/v2/pkg/internal"
+)
+
+type errorMockReader struct {
+       ctrl.Reader
+       listErr error
+       getErr  error
+}
+
+func (m *errorMockReader) List(ctx context.Context, list ctrl.ObjectList, opts 
...ctrl.ListOption) error {
+       if m.listErr != nil {
+               return m.listErr
+       }
+
+       return m.Reader.List(ctx, list, opts...)
+}
+
+func (m *errorMockReader) Get(ctx context.Context, key ctrl.ObjectKey, obj 
ctrl.Object, opts ...ctrl.GetOption) error {
+       if m.getErr != nil {
+               return m.getErr
+       }
+
+       return m.Reader.Get(ctx, key, obj, opts...)
+}
+
+func newUnstructuredClusterIssuer(name string) *unstructured.Unstructured {
+       u := &unstructured.Unstructured{}
+       u.SetGroupVersionKind(ClusterIssuerGVK)
+       u.SetName(name)
+
+       return u
+}
+
+func newUnstructuredIssuer(namespace, name string) *unstructured.Unstructured {
+       u := &unstructured.Unstructured{}
+       u.SetGroupVersionKind(IssuerGVK)
+       u.SetNamespace(namespace)
+       u.SetName(name)
+
+       return u
+}
+
+func TestListClusterIssuers(t *testing.T) {
+       ci1 := newUnstructuredClusterIssuer("letsencrypt-staging")
+       ci2 := newUnstructuredClusterIssuer("letsencrypt-prod")
+
+       c, err := internal.NewFakeClient(ci1, ci2)
+       require.NoError(t, err)
+
+       names, err := ListClusterIssuers(context.Background(), c)
+       require.NoError(t, err)
+       assert.Equal(t, []string{"letsencrypt-prod", "letsencrypt-staging"}, 
names)
+}
+
+func TestGetClusterIssuer(t *testing.T) {
+       ci := newUnstructuredClusterIssuer("my-cluster-issuer")
+
+       c, err := internal.NewFakeClient(ci)
+       require.NoError(t, err)
+
+       exists, err := GetClusterIssuer(context.Background(), c, 
"my-cluster-issuer")
+       require.NoError(t, err)
+       assert.True(t, exists)
+
+       notExists, err := GetClusterIssuer(context.Background(), c, 
"non-existent")
+       require.NoError(t, err)
+       assert.False(t, notExists)
+}
+
+func TestListIssuers(t *testing.T) {
+       i1 := newUnstructuredIssuer("ns1", "issuer-b")
+       i2 := newUnstructuredIssuer("ns1", "issuer-a")
+       i3 := newUnstructuredIssuer("ns2", "issuer-other")
+
+       c, err := internal.NewFakeClient(i1, i2, i3)
+       require.NoError(t, err)
+
+       names, err := ListIssuers(context.Background(), c, "ns1")
+       require.NoError(t, err)
+       assert.Equal(t, []string{"issuer-a", "issuer-b"}, names)
+
+       namesOther, err := ListIssuers(context.Background(), c, "ns2")
+       require.NoError(t, err)
+       assert.Equal(t, []string{"issuer-other"}, namesOther)
+
+       namesEmpty, err := ListIssuers(context.Background(), c, "ns-empty")
+       require.NoError(t, err)
+       assert.Empty(t, namesEmpty)
+}
+
+func TestGetIssuer(t *testing.T) {
+       i := newUnstructuredIssuer("test-ns", "my-issuer")
+
+       c, err := internal.NewFakeClient(i)
+       require.NoError(t, err)
+
+       exists, err := GetIssuer(context.Background(), c, "test-ns", 
"my-issuer")
+       require.NoError(t, err)
+       assert.True(t, exists)
+
+       notExistsWrongNs, err := GetIssuer(context.Background(), c, "wrong-ns", 
"my-issuer")
+       require.NoError(t, err)
+       assert.False(t, notExistsWrongNs)
+
+       notExistsWrongName, err := GetIssuer(context.Background(), c, 
"test-ns", "other-issuer")
+       require.NoError(t, err)
+       assert.False(t, notExistsWrongName)
+}
+
+func TestCRDsAbsentEntirely(t *testing.T) {
+       baseClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+
+       noKindMatchErr := &meta.NoKindMatchError{
+               GroupKind:        schema.GroupKind{Group: CertManagerAPIGroup, 
Kind: "ClusterIssuerList"},
+               SearchedVersions: []string{"v1"},
+       }
+
+       reader := &errorMockReader{
+               Reader:  baseClient,
+               listErr: noKindMatchErr,
+               getErr:  noKindMatchErr,
+       }
+
+       // 1. ListClusterIssuers should return (nil, nil)
+       ciList, err := ListClusterIssuers(context.Background(), reader)
+       assert.NoError(t, err)
+       assert.Nil(t, ciList)
+
+       // 2. ListIssuers should return (nil, nil)
+       iList, err := ListIssuers(context.Background(), reader, "my-ns")
+       assert.NoError(t, err)
+       assert.Nil(t, iList)
+
+       // 3. GetClusterIssuer should return (false, nil)
+       ciExists, err := GetClusterIssuer(context.Background(), reader, "my-ci")
+       assert.NoError(t, err)
+       assert.False(t, ciExists)
+
+       // 4. GetIssuer should return (false, nil)
+       iExists, err := GetIssuer(context.Background(), reader, "my-ns", "my-i")
+       assert.NoError(t, err)
+       assert.False(t, iExists)
+}
+
+func TestCRDsAbsentViaUnknownAPIError(t *testing.T) {
+       baseClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+
+       unknownAPIErr := fmt.Errorf("no matches for kind \"ClusterIssuerList\" 
in version \"cert-manager.io/v1\"")
+
+       reader := &errorMockReader{
+               Reader:  baseClient,
+               listErr: unknownAPIErr,
+               getErr:  unknownAPIErr,
+       }
+
+       ciList, err := ListClusterIssuers(context.Background(), reader)
+       assert.NoError(t, err)
+       assert.Nil(t, ciList)
+
+       ciExists, err := GetClusterIssuer(context.Background(), reader, "my-ci")
+       assert.NoError(t, err)
+       assert.False(t, ciExists)
+}
+
+func TestCRDsAbsentViaNotFoundError(t *testing.T) {
+       baseClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+
+       notFoundErr := k8serrors.NewNotFound(
+               schema.GroupResource{Group: CertManagerAPIGroup, Resource: 
"clusterissuers"},
+               "my-ci",
+       )
+
+       reader := &errorMockReader{
+               Reader:  baseClient,
+               listErr: notFoundErr,
+               getErr:  notFoundErr,
+       }
+
+       ciList, err := ListClusterIssuers(context.Background(), reader)
+       assert.NoError(t, err)
+       assert.Nil(t, ciList)
+
+       ciExists, err := GetClusterIssuer(context.Background(), reader, "my-ci")
+       assert.NoError(t, err)
+       assert.False(t, ciExists)
+}
+
+func TestGenericErrorPropagated(t *testing.T) {
+       baseClient, err := internal.NewFakeClient()
+       require.NoError(t, err)
+
+       authErr := errors.New("connection refused: dial tcp 10.0.0.1:443")
+
+       reader := &errorMockReader{
+               Reader:  baseClient,
+               listErr: authErr,
+               getErr:  authErr,
+       }
+
+       // Any non-absence error MUST be returned and NOT swallowed
+       ciList, err := ListClusterIssuers(context.Background(), reader)
+       assert.ErrorIs(t, err, authErr)
+       assert.Nil(t, ciList)
+
+       iList, err := ListIssuers(context.Background(), reader, "my-ns")
+       assert.ErrorIs(t, err, authErr)
+       assert.Nil(t, iList)
+
+       ciExists, err := GetClusterIssuer(context.Background(), reader, "my-ci")
+       assert.ErrorIs(t, err, authErr)
+       assert.False(t, ciExists)
+
+       iExists, err := GetIssuer(context.Background(), reader, "my-ns", "my-i")
+       assert.ErrorIs(t, err, authErr)
+       assert.False(t, iExists)
+}
diff --git a/script/Makefile b/script/Makefile
index 478678972..16cc56de7 100644
--- a/script/Makefile
+++ b/script/Makefile
@@ -339,6 +339,12 @@ test-telemetry:
 test-gateway:
        go test -timeout 10m -v ./e2e/gateway -tags=integration $(GOTESTFMT)
 
+#
+# Cert-manager tests that require the presence of cert-manager
+#
+test-cert-manager:
+       go test -timeout 10m -v ./e2e/cert-manager -tags=integration 
$(GOTESTFMT)
+
 #
 # Quarkus native test (requires certain CPU and memory conditions)
 #

Reply via email to