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
