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

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

commit c61f1400dc78617b9cee46270d62e5ade1572134
Author: Antonin Stefanutti <[email protected]>
AuthorDate: Tue Apr 13 18:47:31 2021 +0200

    feat(monitoring): Migrate to PodMonitor in Prometheus trait
---
 e2e/common/operator_metrics_test.go  |  47 ++++++------
 e2e/common/traits/prometheus_test.go |  36 ++++-----
 pkg/cmd/run_test.go                  |   4 +-
 pkg/trait/container.go               |  43 ++++++-----
 pkg/trait/prometheus.go              | 139 ++++++++++++++---------------------
 pkg/trait/prometheus_test.go         |  82 ++++-----------------
 pkg/trait/route.go                   |   2 +-
 pkg/trait/route_test.go              |   2 +-
 pkg/trait/service.go                 |   5 +-
 pkg/trait/trait_types.go             |  24 ++++++
 pkg/util/kubernetes/collection.go    |  14 ++--
 11 files changed, 167 insertions(+), 231 deletions(-)

diff --git a/e2e/common/operator_metrics_test.go 
b/e2e/common/operator_metrics_test.go
index 7814eef..fa10779 100644
--- a/e2e/common/operator_metrics_test.go
+++ b/e2e/common/operator_metrics_test.go
@@ -31,14 +31,15 @@ import (
        . "github.com/onsi/gomega"
        . "github.com/onsi/gomega/gstruct"
        "github.com/onsi/gomega/types"
-       v1 "k8s.io/api/core/v1"
+
+       corev1 "k8s.io/api/core/v1"
 
        prometheus "github.com/prometheus/client_model/go"
        "github.com/prometheus/common/expfmt"
 
        . "github.com/apache/camel-k/e2e/support"
        . "github.com/apache/camel-k/e2e/support/util"
-       camelv1 "github.com/apache/camel-k/pkg/apis/camel/v1"
+       v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
 )
 
 func TestMetrics(t *testing.T) {
@@ -47,15 +48,15 @@ func TestMetrics(t *testing.T) {
                Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
                Expect(Kamel("run", "-n", ns, "files/Java.java",
                        "-t", "prometheus.enabled=true",
-                       "-t", 
"prometheus.service-monitor=false").Execute()).To(Succeed())
-               Eventually(IntegrationPodPhase(ns, name), 
TestTimeoutMedium).Should(Equal(v1.PodRunning))
-               Eventually(IntegrationCondition(ns, name, 
camelv1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(v1.ConditionTrue))
+                       "-t", 
"prometheus.pod-monitor=false").Execute()).To(Succeed())
+               Eventually(IntegrationPodPhase(ns, name), 
TestTimeoutMedium).Should(Equal(corev1.PodRunning))
+               Eventually(IntegrationCondition(ns, name, 
v1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
                Eventually(IntegrationLogs(ns, "java"), 
TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
 
                pod := OperatorPod(ns)()
                Expect(pod).NotTo(BeNil())
 
-               logs := StructuredLogs(ns, pod.Name, v1.PodLogOptions{})
+               logs := StructuredLogs(ns, pod.Name, corev1.PodLogOptions{})
                Expect(logs).NotTo(BeEmpty())
 
                response, err := TestClient().CoreV1().RESTClient().Get().
@@ -80,7 +81,7 @@ func TestMetrics(t *testing.T) {
                                AddStep(MatchFields(IgnoreExtras, Fields{
                                        "LoggerName":  
Equal("camel-k.controller.build"),
                                        "Message":     Equal("Build state 
transition"),
-                                       "Phase":       
Equal(string(camelv1.BuildPhasePending)),
+                                       "Phase":       
Equal(string(v1.BuildPhasePending)),
                                        "RequestName": Equal(build.Name),
                                }), LogEntryNoop).
                                AddStep(MatchFields(IgnoreExtras, Fields{
@@ -188,8 +189,8 @@ func TestMetrics(t *testing.T) {
                        platformReconciled := 
getMetric(metrics["camel_k_reconciliation_duration_seconds"],
                                MatchFieldsP(IgnoreExtras, Fields{
                                        "Label": ConsistOf(
-                                               label("group", 
camelv1.SchemeGroupVersion.Group),
-                                               label("version", 
camelv1.SchemeGroupVersion.Version),
+                                               label("group", 
v1.SchemeGroupVersion.Group),
+                                               label("version", 
v1.SchemeGroupVersion.Version),
                                                label("kind", 
"IntegrationPlatform"),
                                                label("namespace", ns),
                                                label("result", "Reconciled"),
@@ -203,8 +204,8 @@ func TestMetrics(t *testing.T) {
                        platformRequeued := 
getMetric(metrics["camel_k_reconciliation_duration_seconds"],
                                MatchFieldsP(IgnoreExtras, Fields{
                                        "Label": ConsistOf(
-                                               label("group", 
camelv1.SchemeGroupVersion.Group),
-                                               label("version", 
camelv1.SchemeGroupVersion.Version),
+                                               label("group", 
v1.SchemeGroupVersion.Group),
+                                               label("version", 
v1.SchemeGroupVersion.Version),
                                                label("kind", 
"IntegrationPlatform"),
                                                label("namespace", ns),
                                                label("result", "Requeued"),
@@ -236,8 +237,8 @@ func TestMetrics(t *testing.T) {
                                        "Type": 
EqualP(prometheus.MetricType_HISTOGRAM),
                                        "Metric": 
ContainElement(MatchFieldsP(IgnoreExtras, Fields{
                                                "Label": ConsistOf(
-                                                       label("group", 
camelv1.SchemeGroupVersion.Group),
-                                                       label("version", 
camelv1.SchemeGroupVersion.Version),
+                                                       label("group", 
v1.SchemeGroupVersion.Group),
+                                                       label("version", 
v1.SchemeGroupVersion.Version),
                                                        label("kind", 
"Integration"),
                                                        label("namespace", 
it.Namespace),
                                                        label("result", 
"Reconciled"),
@@ -268,8 +269,8 @@ func TestMetrics(t *testing.T) {
                                        "Type": 
EqualP(prometheus.MetricType_HISTOGRAM),
                                        "Metric": 
ContainElement(MatchFieldsP(IgnoreExtras, Fields{
                                                "Label": ConsistOf(
-                                                       label("group", 
camelv1.SchemeGroupVersion.Group),
-                                                       label("version", 
camelv1.SchemeGroupVersion.Version),
+                                                       label("group", 
v1.SchemeGroupVersion.Group),
+                                                       label("version", 
v1.SchemeGroupVersion.Version),
                                                        label("kind", 
"IntegrationKit"),
                                                        label("namespace", 
it.Status.IntegrationKit.Namespace),
                                                        label("result", 
"Reconciled"),
@@ -295,8 +296,8 @@ func TestMetrics(t *testing.T) {
                        buildReconciled := 
getMetric(metrics["camel_k_reconciliation_duration_seconds"],
                                MatchFieldsP(IgnoreExtras, Fields{
                                        "Label": ConsistOf(
-                                               label("group", 
camelv1.SchemeGroupVersion.Group),
-                                               label("version", 
camelv1.SchemeGroupVersion.Version),
+                                               label("group", 
v1.SchemeGroupVersion.Group),
+                                               label("version", 
v1.SchemeGroupVersion.Version),
                                                label("kind", "Build"),
                                                label("namespace", 
build.Namespace),
                                                label("result", "Reconciled"),
@@ -310,8 +311,8 @@ func TestMetrics(t *testing.T) {
                        buildRequeued := 
getMetric(metrics["camel_k_reconciliation_duration_seconds"],
                                MatchFieldsP(IgnoreExtras, Fields{
                                        "Label": ConsistOf(
-                                               label("group", 
camelv1.SchemeGroupVersion.Group),
-                                               label("version", 
camelv1.SchemeGroupVersion.Version),
+                                               label("group", 
v1.SchemeGroupVersion.Group),
+                                               label("version", 
v1.SchemeGroupVersion.Version),
                                                label("kind", "Build"),
                                                label("namespace", 
build.Namespace),
                                                label("result", "Requeued"),
@@ -336,7 +337,7 @@ func TestMetrics(t *testing.T) {
                                AddStep(MatchFields(IgnoreExtras, Fields{
                                        "LoggerName":  
Equal("camel-k.controller.build"),
                                        "Message":     Equal("Build state 
transition"),
-                                       "Phase":       
Equal(string(camelv1.BuildPhasePending)),
+                                       "Phase":       
Equal(string(v1.BuildPhasePending)),
                                        "RequestName": Equal(build.Name),
                                }), func(l *LogEntry) { ts2 = l.Timestamp.Time 
}).
                                Walk()
@@ -385,7 +386,7 @@ func TestMetrics(t *testing.T) {
                        ts1 = it.Status.InitializationTimestamp.Time
                        Expect(ts1).NotTo(BeZero())
                        // The end time is reported into the ready condition 
first truthy time
-                       ts2 = 
it.Status.GetCondition(camelv1.IntegrationConditionReady).FirstTruthyTime.Time
+                       ts2 = 
it.Status.GetCondition(v1.IntegrationConditionReady).FirstTruthyTime.Time
                        Expect(ts2).NotTo(BeZero())
 
                        duration := ts2.Sub(ts1)
@@ -396,8 +397,8 @@ func TestMetrics(t *testing.T) {
                                        "LoggerName":  
Equal("camel-k.controller.integration"),
                                        "Message":     Equal("Reconciling 
Integration"),
                                        "RequestName": Equal(it.Name),
-                                       "PhaseFrom":   
Equal(string(camelv1.IntegrationPhaseInitialization)),
-                                       "PhaseTo":     
Equal(string(camelv1.IntegrationPhaseBuildingKit)),
+                                       "PhaseFrom":   
Equal(string(v1.IntegrationPhaseInitialization)),
+                                       "PhaseTo":     
Equal(string(v1.IntegrationPhaseBuildingKit)),
                                }), func(l *LogEntry) { ts1 = l.Timestamp.Time 
}).
                                AddStep(MatchFields(IgnoreExtras, Fields{
                                        "LoggerName":  
Equal("camel-k.controller.integration"),
diff --git a/e2e/common/traits/prometheus_test.go 
b/e2e/common/traits/prometheus_test.go
index e3204ca..91ce909 100644
--- a/e2e/common/traits/prometheus_test.go
+++ b/e2e/common/traits/prometheus_test.go
@@ -29,9 +29,9 @@ import (
        "github.com/stretchr/testify/assert"
 
        v1 "k8s.io/api/core/v1"
-       k8serrors "k8s.io/apimachinery/pkg/api/errors"
+       "k8s.io/apimachinery/pkg/api/errors"
 
-       k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
+       ctrl "sigs.k8s.io/controller-runtime/pkg/client"
 
        monitoringv1 
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
 
@@ -45,14 +45,14 @@ func TestPrometheusTrait(t *testing.T) {
                ocp, err := openshift.IsOpenShift(TestClient())
                assert.Nil(t, err)
 
-               // suppress Service Monitor for the time being as CI test runs 
on OCP 3.11
-               createServiceMonitor := false
+               // Do not create PodMonitor for the time being as CI test runs 
on OCP 3.11
+               createPodMonitor := false
 
                Expect(Kamel("install", "-n", ns).Execute()).To(Succeed())
 
                Expect(Kamel("run", "-n", ns, "files/Java.java",
                        "-t", "prometheus.enabled=true",
-                       "-t", fmt.Sprintf("prometheus.service-monitor=%v", 
createServiceMonitor)).Execute()).To(Succeed())
+                       "-t", fmt.Sprintf("prometheus.pod-monitor=%v", 
createPodMonitor)).Execute()).To(Succeed())
                Eventually(IntegrationPodPhase(ns, "java"), 
TestTimeoutLong).Should(Equal(v1.PodRunning))
                Eventually(IntegrationCondition(ns, "java", 
camelv1.IntegrationConditionReady), 
TestTimeoutShort).Should(Equal(v1.ConditionTrue))
                Eventually(IntegrationLogs(ns, "java"), 
TestTimeoutShort).Should(ContainSubstring("Magicstring!"))
@@ -67,15 +67,9 @@ func TestPrometheusTrait(t *testing.T) {
                        assert.Contains(t, string(response), 
"camel.route.exchanges.total")
                })
 
-               t.Run("Service is created", func(t *testing.T) {
-                       // service name is "<integration name>-prometheus"
-                       service := Service(ns, "java-prometheus")
-                       Eventually(service, TestTimeoutShort).ShouldNot(BeNil())
-               })
-
-               if ocp && createServiceMonitor {
-                       t.Run("Service Monitor is created on OpenShift", func(t 
*testing.T) {
-                               sm := serviceMonitor(ns, "java")
+               if ocp && createPodMonitor {
+                       t.Run("PodMonitor is created", func(t *testing.T) {
+                               sm := podMonitor(ns, "java")
                                Eventually(sm, 
TestTimeoutShort).ShouldNot(BeNil())
                        })
                }
@@ -84,19 +78,19 @@ func TestPrometheusTrait(t *testing.T) {
        })
 }
 
-func serviceMonitor(ns string, name string) func() 
*monitoringv1.ServiceMonitor {
-       return func() *monitoringv1.ServiceMonitor {
-               sm := monitoringv1.ServiceMonitor{}
-               key := k8sclient.ObjectKey{
+func podMonitor(ns string, name string) func() *monitoringv1.PodMonitor {
+       return func() *monitoringv1.PodMonitor {
+               pm := monitoringv1.PodMonitor{}
+               key := ctrl.ObjectKey{
                        Namespace: ns,
                        Name:      name,
                }
-               err := TestClient().Get(TestContext, key, &sm)
-               if err != nil && k8serrors.IsNotFound(err) {
+               err := TestClient().Get(TestContext, key, &pm)
+               if err != nil && errors.IsNotFound(err) {
                        return nil
                } else if err != nil {
                        panic(err)
                }
-               return &sm
+               return &pm
        }
 }
diff --git a/pkg/cmd/run_test.go b/pkg/cmd/run_test.go
index eddb32f..212765b 100644
--- a/pkg/cmd/run_test.go
+++ b/pkg/cmd/run_test.go
@@ -353,7 +353,7 @@ func TestConfigureTraits(t *testing.T) {
                "--trait", "container.probes-enabled=false",
                "--trait", "environment.container-meta=false",
                "--trait", "jvm.print-command=false",
-               "--trait", "prometheus.service-monitor=false",
+               "--trait", "prometheus.pod-monitor=false",
                "example.js")
        if err != nil {
                t.Error(err)
@@ -372,7 +372,7 @@ func TestConfigureTraits(t *testing.T) {
        assertTraitConfiguration(t, traits, "container", 
`{"probesEnabled":false}`)
        assertTraitConfiguration(t, traits, "environment", 
`{"containerMeta":false}`)
        assertTraitConfiguration(t, traits, "jvm", `{"printCommand":false}`)
-       assertTraitConfiguration(t, traits, "prometheus", 
`{"serviceMonitor":false}`)
+       assertTraitConfiguration(t, traits, "prometheus", 
`{"podMonitor":false}`)
 }
 
 func assertTraitConfiguration(t *testing.T, traits map[string]v1.TraitSpec, 
trait string, expected string) {
diff --git a/pkg/trait/container.go b/pkg/trait/container.go
index 238a827..11dcb61 100644
--- a/pkg/trait/container.go
+++ b/pkg/trait/container.go
@@ -36,11 +36,12 @@ import (
 )
 
 const (
-       defaultContainerName = "integration"
-       defaultContainerPort = 8080
-       defaultServicePort   = 80
-       defaultProbePath     = "/health"
-       containerTraitID     = "container"
+       defaultContainerName     = "integration"
+       defaultContainerPort     = 8080
+       defaultContainerPortName = "http"
+       defaultServicePort       = 80
+       defaultProbePath         = "/health"
+       containerTraitID         = "container"
 )
 
 // The Container trait can be used to configure properties of the container 
where the integration will run.
@@ -111,9 +112,8 @@ func newContainerTrait() Trait {
        return &containerTrait{
                BaseTrait:       NewBaseTrait(containerTraitID, 1600),
                Port:            defaultContainerPort,
-               PortName:        httpPortName,
                ServicePort:     defaultServicePort,
-               ServicePortName: httpPortName,
+               ServicePortName: defaultContainerPortName,
                Name:            defaultContainerName,
                ProbesEnabled:   util.BoolP(false),
                ProbePath:       defaultProbePath,
@@ -205,11 +205,13 @@ func (t *containerTrait) configureContainer(e 
*Environment) error {
                return err
        }
 
-       //
+       portName := t.PortName
+       if portName == "" {
+               portName = defaultContainerPortName
+       }
        // Deployment
-       //
        if err := e.Resources.VisitDeploymentE(func(deployment 
*appsv1.Deployment) error {
-               if util.IsTrue(t.ProbesEnabled) && t.PortName == httpPortName {
+               if util.IsTrue(t.ProbesEnabled) && portName == 
defaultContainerPortName {
                        if err := t.configureProbes(e, &container, t.Port, 
t.ProbePath); err != nil {
                                return err
                        }
@@ -234,11 +236,9 @@ func (t *containerTrait) configureContainer(e 
*Environment) error {
                return err
        }
 
-       //
        // Knative Service
-       //
        if err := e.Resources.VisitKnativeServiceE(func(service 
*serving.Service) error {
-               if util.IsTrue(t.ProbesEnabled) && t.PortName == httpPortName {
+               if util.IsTrue(t.ProbesEnabled) && portName == 
defaultContainerPortName {
                        // don't set the port on Knative service as it is not 
allowed.
                        if err := t.configureProbes(e, &container, 0, 
t.ProbePath); err != nil {
                                return err
@@ -275,11 +275,9 @@ func (t *containerTrait) configureContainer(e 
*Environment) error {
                return err
        }
 
-       //
        // CronJob
-       //
        if err := e.Resources.VisitCronJobE(func(cron *v1beta1.CronJob) error {
-               if util.IsTrue(t.ProbesEnabled) && t.PortName == httpPortName {
+               if util.IsTrue(t.ProbesEnabled) && portName == 
defaultContainerPortName {
                        if err := t.configureProbes(e, &container, t.Port, 
t.ProbePath); err != nil {
                                return err
                        }
@@ -313,8 +311,13 @@ func (t *containerTrait) configureService(e *Environment, 
container *corev1.Cont
                return
        }
 
+       name := t.PortName
+       if name == "" {
+               name = defaultContainerPortName
+       }
+
        containerPort := corev1.ContainerPort{
-               Name:          t.PortName,
+               Name:          name,
                ContainerPort: int32(t.Port),
                Protocol:      corev1.ProtocolTCP,
        }
@@ -323,7 +326,7 @@ func (t *containerTrait) configureService(e *Environment, 
container *corev1.Cont
                Name:       t.ServicePortName,
                Port:       int32(t.ServicePort),
                Protocol:   corev1.ProtocolTCP,
-               TargetPort: intstr.FromString(t.PortName),
+               TargetPort: intstr.FromString(name),
        }
 
        e.Integration.Status.SetCondition(
@@ -345,9 +348,7 @@ func (t *containerTrait) configureService(e *Environment, 
container *corev1.Cont
 }
 
 func (t *containerTrait) configureResources(_ *Environment, container 
*corev1.Container) {
-       //
        // Requests
-       //
        if container.Resources.Requests == nil {
                container.Resources.Requests = make(corev1.ResourceList)
        }
@@ -369,9 +370,7 @@ func (t *containerTrait) configureResources(_ *Environment, 
container *corev1.Co
                }
        }
 
-       //
        // Limits
-       //
        if container.Resources.Limits == nil {
                container.Resources.Limits = make(corev1.ResourceList)
        }
diff --git a/pkg/trait/prometheus.go b/pkg/trait/prometheus.go
index 7a5a87d..8ef81b5 100644
--- a/pkg/trait/prometheus.go
+++ b/pkg/trait/prometheus.go
@@ -22,7 +22,6 @@ import (
 
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-       "k8s.io/apimachinery/pkg/util/intstr"
 
        monitoringv1 
"github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
 
@@ -30,35 +29,30 @@ import (
        "github.com/apache/camel-k/pkg/util"
 )
 
-// The Prometheus trait configures a Prometheus-compatible endpoint. This 
trait also exposes the integration with
-// `Service` and `ServiceMonitor` resources, so that the endpoint can be 
scraped automatically, when using the
-// Prometheus Operator.
+// The Prometheus trait configures a Prometheus-compatible endpoint. It also 
creates a `PodMonitor` resource,
+// so that the endpoint can be scraped automatically, when using the 
Prometheus operator.
 //
 // The metrics are exposed using MicroProfile Metrics.
 //
-// WARNING: The creation of the `ServiceMonitor` resource requires the 
https://github.com/coreos/prometheus-operator[Prometheus Operator]
+// WARNING: The creation of the `PodMonitor` resource requires the 
https://github.com/coreos/prometheus-operator[Prometheus Operator]
 // custom resource definition to be installed.
-// You can set `service-monitor` to `false` for the Prometheus trait to work 
without the Prometheus Operator.
+// You can set `pod-monitor` to `false` for the Prometheus trait to work 
without the Prometheus Operator.
 //
 // The Prometheus trait is disabled by default.
 //
 // +camel-k:trait=prometheus
 type prometheusTrait struct {
        BaseTrait `property:",squash"`
-       // The Prometheus endpoint port (default `9779`, or `8080` with 
Quarkus).
-       Port *int `property:"port" json:"port,omitempty"`
-       // Whether a `ServiceMonitor` resource is created (default `true`).
-       ServiceMonitor *bool `property:"service-monitor" 
json:"serviceMonitor,omitempty"`
-       // The `ServiceMonitor` resource labels, applicable when 
`service-monitor` is `true`.
-       ServiceMonitorLabels []string `property:"service-monitor-labels" 
json:"serviceMonitorLabels,omitempty"`
+       // Whether a `PodMonitor` resource is created (default `true`).
+       PodMonitor *bool `property:"pod-monitor" json:"podMonitor,omitempty"`
+       // The `PodMonitor` resource labels, applicable when `pod-monitor` is 
`true`.
+       PodMonitorLabels []string `property:"pod-monitor-labels" 
json:"podMonitorLabels,omitempty"`
 }
 
-const prometheusPortName = "prometheus"
-
 func newPrometheusTrait() Trait {
        return &prometheusTrait{
-               BaseTrait:      NewBaseTrait("prometheus", 1900),
-               ServiceMonitor: util.BoolP(true),
+               BaseTrait:  NewBaseTrait("prometheus", 1900),
+               PodMonitor: util.BoolP(true),
        }
 }
 
@@ -70,7 +64,7 @@ func (t *prometheusTrait) Configure(e *Environment) (bool, 
error) {
        ), nil
 }
 
-func (t *prometheusTrait) Apply(e *Environment) error {
+func (t *prometheusTrait) Apply(e *Environment) (err error) {
        if e.IntegrationInPhase(v1.IntegrationPhaseInitialization) {
                // Add the Camel Quarkus MP Metrics extension
                util.StringSliceUniqueAdd(&e.Integration.Status.Dependencies, 
"mvn:org.apache.camel.quarkus:camel-quarkus-microprofile-metrics")
@@ -94,60 +88,38 @@ func (t *prometheusTrait) Apply(e *Environment) error {
                Reason: v1.IntegrationConditionPrometheusAvailableReason,
        }
 
-       port := 8080
-       if t.Port == nil {
-               t.Port = &port
-       }
-
-       // Configure the Prometheus container port
-       containerPort := t.getContainerPort()
        controller, err := e.DetermineControllerStrategy()
        if err != nil {
                return err
        }
-       // Skip declaring the Prometheus port when Knative is enabled, as only 
one container port is supported
-       if controller != ControllerStrategyKnativeService {
+
+       containerPort := e.getIntegrationContainerPort()
+       if containerPort == nil {
+               containerPort = t.getContainerPort(e, controller)
                container.Ports = append(container.Ports, *containerPort)
        }
+
        condition.Message = fmt.Sprintf("%s(%d)", container.Name, 
containerPort.ContainerPort)
 
-       // Retrieve the service or create a new one if the service trait is 
enabled
-       serviceEnabled := false
-       service := e.Resources.GetServiceForIntegration(e.Integration)
-       if service == nil {
-               trait := e.Catalog.GetTrait(serviceTraitID)
-               if serviceTrait, ok := trait.(*serviceTrait); ok {
-                       serviceEnabled = serviceTrait.isEnabled()
-               }
-               if serviceEnabled {
-                       // Add a new service if not already created
-                       service = getServiceFor(e)
-                       // Override the service name if none exists.
-                       // This is required for Knative Serving, that checks no 
standard eponymous service exist
-                       service.Name += "-prometheus"
-                       e.Resources.Add(service)
+       // Add the PodMonitor resource
+       if util.IsTrue(t.PodMonitor) {
+               portName := containerPort.Name
+               // Knative defaults to naming the userland container port 
"user-port".
+               // Let's rely on that default, granted it is not officially 
part of the Knative
+               // runtime contract.
+               // See 
https://github.com/knative/specs/blob/main/specs/serving/runtime-contract.md
+               if portName == "" && controller == 
ControllerStrategyKnativeService {
+                       portName = "user-port"
                }
-       } else {
-               serviceEnabled = true
-       }
 
-       // Add the service port and service monitor resource
-       if serviceEnabled {
-               servicePort := t.getServicePort()
-               service.Spec.Ports = append(service.Spec.Ports, *servicePort)
-               condition.Message = fmt.Sprintf("%s(%s/%d) -> ", service.Name, 
servicePort.Name, servicePort.Port) + condition.Message
-
-               // Add the ServiceMonitor resource
-               if util.IsNilOrTrue(t.ServiceMonitor) {
-                       smt, err := t.getServiceMonitorFor(e)
-                       if err != nil {
-                               return err
-                       }
-                       e.Resources.Add(smt)
+               podMonitor, err := t.getPodMonitorFor(e, portName)
+               if err != nil {
+                       return err
                }
+               e.Resources.Add(podMonitor)
+               condition.Message = fmt.Sprintf("PodMonitor (%s) -> ", 
podMonitor.Name) + condition.Message
        } else {
-               condition.Status = corev1.ConditionFalse
-               condition.Reason = 
v1.IntegrationConditionServiceNotAvailableReason
+               condition.Message = "ContainerPort " + condition.Message
        }
 
        e.Integration.Status.SetConditions(condition)
@@ -155,55 +127,58 @@ func (t *prometheusTrait) Apply(e *Environment) error {
        return nil
 }
 
-func (t *prometheusTrait) getContainerPort() *corev1.ContainerPort {
-       containerPort := corev1.ContainerPort{
-               ContainerPort: int32(*t.Port),
-               Protocol:      corev1.ProtocolTCP,
+func (t *prometheusTrait) getContainerPort(e *Environment, controller 
ControllerStrategy) *corev1.ContainerPort {
+       var name string
+       var port int
+
+       if t := e.Catalog.GetTrait(containerTraitID); t != nil {
+               name = t.(*containerTrait).PortName
+               port = t.(*containerTrait).Port
        }
-       return &containerPort
-}
 
-func (t *prometheusTrait) getServicePort() *corev1.ServicePort {
-       servicePort := corev1.ServicePort{
-               Name:     prometheusPortName,
-               Port:     int32(*t.Port),
-               Protocol: corev1.ProtocolTCP,
-               // Avoid relying on named port, as Knative enforces specific 
values used for content negotiation
-               TargetPort: intstr.FromInt(*t.Port),
+       // Let's rely on Knative default HTTP negotiation
+       if name == "" && controller != ControllerStrategyKnativeService {
+               name = defaultContainerPortName
+       }
+
+       return &corev1.ContainerPort{
+               Name:          name,
+               ContainerPort: int32(port),
+               Protocol:      corev1.ProtocolTCP,
        }
-       return &servicePort
 }
 
-func (t *prometheusTrait) getServiceMonitorFor(e *Environment) 
(*monitoringv1.ServiceMonitor, error) {
-       labels, err := keyValuePairArrayAsStringMap(t.ServiceMonitorLabels)
+func (t *prometheusTrait) getPodMonitorFor(e *Environment, portName string) 
(*monitoringv1.PodMonitor, error) {
+       labels, err := keyValuePairArrayAsStringMap(t.PodMonitorLabels)
        if err != nil {
                return nil, err
        }
        labels[v1.IntegrationLabel] = e.Integration.Name
 
-       smt := monitoringv1.ServiceMonitor{
+       podMonitor := monitoringv1.PodMonitor{
                TypeMeta: metav1.TypeMeta{
-                       Kind:       "ServiceMonitor",
-                       APIVersion: "monitoring.coreos.com/v1",
+                       Kind:       "PodMonitor",
+                       APIVersion: monitoringv1.SchemeGroupVersion.String(),
                },
                ObjectMeta: metav1.ObjectMeta{
                        Name:      e.Integration.Name,
                        Namespace: e.Integration.Namespace,
                        Labels:    labels,
                },
-               Spec: monitoringv1.ServiceMonitorSpec{
+               Spec: monitoringv1.PodMonitorSpec{
                        Selector: metav1.LabelSelector{
                                MatchLabels: map[string]string{
                                        v1.IntegrationLabel: e.Integration.Name,
                                },
                        },
-                       Endpoints: []monitoringv1.Endpoint{
+                       PodMetricsEndpoints: []monitoringv1.PodMetricsEndpoint{
                                {
+                                       Port: portName,
                                        Path: "/q/metrics",
-                                       Port: prometheusPortName,
                                },
                        },
                },
        }
-       return &smt, nil
+
+       return &podMonitor, nil
 }
diff --git a/pkg/trait/prometheus_test.go b/pkg/trait/prometheus_test.go
index 1bf8d65..670a911 100644
--- a/pkg/trait/prometheus_test.go
+++ b/pkg/trait/prometheus_test.go
@@ -70,16 +70,10 @@ func TestApplyNominalPrometheusTraitDoesSucceed(t 
*testing.T) {
        assert.Equal(t, int32(8080), ports[0].ContainerPort)
        assert.Equal(t, corev1.ProtocolTCP, ports[0].Protocol)
 
-       service := environment.Resources.GetService(func(service 
*corev1.Service) bool {
-               return service.Name == "integration-name-prometheus"
+       podMonitor := environment.Resources.GetPodMonitor(func(pm 
*monitoringv1.PodMonitor) bool {
+               return pm.Name == "integration-name"
        })
-       assert.NotNil(t, service)
-       assert.Len(t, service.Spec.Ports, 1)
-
-       serviceMonitor := environment.Resources.GetServiceMonitor(func(service 
*monitoringv1.ServiceMonitor) bool {
-               return service.Name == "integration-name"
-       })
-       assert.NotNil(t, serviceMonitor)
+       assert.NotNil(t, podMonitor)
 
        assert.Len(t, environment.Integration.Status.Conditions, 1)
        condition := environment.Integration.Status.Conditions[0]
@@ -101,70 +95,22 @@ func 
TestApplyPrometheusTraitWithoutContainerDoesNotSucceed(t *testing.T) {
        assert.Equal(t, corev1.ConditionFalse, condition.Status)
 }
 
-func TestApplyPrometheusTraitWithServiceDoesSucceed(t *testing.T) {
-       trait, environment := createNominalPrometheusTest()
-       environment.Resources = kubernetes.NewCollection(
-               &appsv1.Deployment{
-                       Spec: appsv1.DeploymentSpec{
-                               Template: corev1.PodTemplateSpec{
-                                       Spec: corev1.PodSpec{
-                                               Containers: []corev1.Container{
-                                                       {
-                                                               Name: 
defaultContainerName,
-                                                       },
-                                               },
-                                       },
-                               },
-                       },
-               },
-               &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{},
-                               Selector: map[string]string{
-                                       v1.IntegrationLabel: "integration-name",
-                               },
-                       },
-               })
-
-       err := trait.Apply(environment)
-
-       assert.Nil(t, err)
-
-       assert.Len(t, environment.Integration.Status.Conditions, 1)
-       condition := environment.Integration.Status.Conditions[0]
-       assert.Equal(t, v1.IntegrationConditionPrometheusAvailableReason, 
condition.Reason)
-       assert.Equal(t, corev1.ConditionTrue, condition.Status)
-}
-
-func TestPrometheusTraitGetServiceMonitor(t *testing.T) {
+func TestPrometheusTraitGetPodMonitor(t *testing.T) {
        trait, environment := createNominalPrometheusTest()
 
-       serviceMonitor, err := trait.getServiceMonitorFor(environment)
+       podMonitor, err := trait.getPodMonitorFor(environment, 
defaultContainerPortName)
 
        assert.Nil(t, err)
 
-       assert.NotNil(t, serviceMonitor)
-       assert.Equal(t, "ServiceMonitor", serviceMonitor.Kind)
-       assert.Equal(t, "monitoring.coreos.com/v1", serviceMonitor.APIVersion)
-       assert.Equal(t, "integration-name", serviceMonitor.Name)
-       assert.Equal(t, "integration-namespace", serviceMonitor.Namespace)
-       assert.Equal(t, "integration-name", 
serviceMonitor.Labels[v1.IntegrationLabel])
-       assert.Equal(t, "integration-name", 
serviceMonitor.Spec.Selector.MatchLabels[v1.IntegrationLabel])
-       assert.Len(t, serviceMonitor.Spec.Endpoints, 1)
-       assert.Equal(t, "prometheus", serviceMonitor.Spec.Endpoints[0].Port)
-       assert.Equal(t, "/q/metrics", serviceMonitor.Spec.Endpoints[0].Path)
+       assert.NotNil(t, podMonitor)
+       assert.Equal(t, "PodMonitor", podMonitor.Kind)
+       assert.Equal(t, "monitoring.coreos.com/v1", podMonitor.APIVersion)
+       assert.Equal(t, "integration-name", podMonitor.Name)
+       assert.Equal(t, "integration-namespace", podMonitor.Namespace)
+       assert.Equal(t, "integration-name", 
podMonitor.Labels["camel.apache.org/integration"])
+       assert.Equal(t, "integration-name", 
podMonitor.Spec.Selector.MatchLabels["camel.apache.org/integration"])
+       assert.Len(t, podMonitor.Spec.PodMetricsEndpoints, 1)
+       assert.Equal(t, defaultContainerPortName, 
podMonitor.Spec.PodMetricsEndpoints[0].Port)
 }
 
 func createNominalPrometheusTest() (*prometheusTrait, *Environment) {
diff --git a/pkg/trait/route.go b/pkg/trait/route.go
index cd830d6..4cc05f6 100644
--- a/pkg/trait/route.go
+++ b/pkg/trait/route.go
@@ -112,7 +112,7 @@ func (t *routeTrait) Configure(e *Environment) (bool, 
error) {
 }
 
 func (t *routeTrait) Apply(e *Environment) error {
-       servicePortName := httpPortName
+       servicePortName := defaultContainerPortName
        dt := e.Catalog.GetTrait(containerTraitID)
        if dt != nil {
                servicePortName = dt.(*containerTrait).ServicePortName
diff --git a/pkg/trait/route_test.go b/pkg/trait/route_test.go
index a2c85a4..11b7eea 100644
--- a/pkg/trait/route_test.go
+++ b/pkg/trait/route_test.go
@@ -114,7 +114,7 @@ func TestRoute_Default(t *testing.T) {
        assert.NotNil(t, route)
        assert.Nil(t, route.Spec.TLS)
        assert.NotNil(t, route.Spec.Port)
-       assert.Equal(t, httpPortName, route.Spec.Port.TargetPort.StrVal)
+       assert.Equal(t, defaultContainerPortName, 
route.Spec.Port.TargetPort.StrVal)
 }
 
 func TestRoute_Disabled(t *testing.T) {
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index 2ece2fd..61cf4a9 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -40,10 +40,7 @@ type serviceTrait struct {
        NodePort *bool `property:"node-port" json:"nodePort,omitempty"`
 }
 
-const (
-       serviceTraitID = "service"
-       httpPortName   = "http"
-)
+const serviceTraitID = "service"
 
 func newServiceTrait() Trait {
        return &serviceTrait{
diff --git a/pkg/trait/trait_types.go b/pkg/trait/trait_types.go
index b3cd991..2ca1095 100644
--- a/pkg/trait/trait_types.go
+++ b/pkg/trait/trait_types.go
@@ -801,6 +801,30 @@ func (e *Environment) getIntegrationContainer() 
*corev1.Container {
        return e.Resources.GetContainerByName(containerName)
 }
 
+func (e *Environment) getIntegrationContainerPort() *corev1.ContainerPort {
+       container := e.getIntegrationContainer()
+       if container == nil {
+               return nil
+       }
+
+       portName := ""
+       t := e.Catalog.GetTrait(containerTraitID)
+       if t != nil {
+               portName = t.(*containerTrait).PortName
+       }
+       if portName == "" {
+               portName = defaultContainerPortName
+       }
+
+       for i, port := range container.Ports {
+               if port.Name == portName {
+                       return &container.Ports[i]
+               }
+       }
+
+       return nil
+}
+
 func (e *Environment) getAllInterceptors() []string {
        res := make([]string, 0)
        util.StringSliceUniqueConcat(&res, e.Interceptors)
diff --git a/pkg/util/kubernetes/collection.go 
b/pkg/util/kubernetes/collection.go
index 008bb66..c5d75dc 100644
--- a/pkg/util/kubernetes/collection.go
+++ b/pkg/util/kubernetes/collection.go
@@ -476,19 +476,19 @@ func (c *Collection) Remove(selector func(runtime.Object) 
bool) runtime.Object {
        return nil
 }
 
-func (c *Collection) VisitServiceMonitor(visitor 
func(*monitoringv1.ServiceMonitor)) {
+func (c *Collection) VisitPodMonitor(visitor func(*monitoringv1.PodMonitor)) {
        c.Visit(func(res runtime.Object) {
-               if conv, ok := res.(*monitoringv1.ServiceMonitor); ok {
+               if conv, ok := res.(*monitoringv1.PodMonitor); ok {
                        visitor(conv)
                }
        })
 }
 
-func (c *Collection) GetServiceMonitor(filter 
func(*monitoringv1.ServiceMonitor) bool) *monitoringv1.ServiceMonitor {
-       var retValue *monitoringv1.ServiceMonitor
-       c.VisitServiceMonitor(func(serviceMonitor *monitoringv1.ServiceMonitor) 
{
-               if filter(serviceMonitor) {
-                       retValue = serviceMonitor
+func (c *Collection) GetPodMonitor(filter func(*monitoringv1.PodMonitor) bool) 
*monitoringv1.PodMonitor {
+       var retValue *monitoringv1.PodMonitor
+       c.VisitPodMonitor(func(podMonitor *monitoringv1.PodMonitor) {
+               if filter(podMonitor) {
+                       retValue = podMonitor
                }
        })
        return retValue

Reply via email to