@@ -24,12 +24,14 @@ import (
2424 "time"
2525
2626 apierrors "k8s.io/apimachinery/pkg/api/errors"
27+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728 "k8s.io/apimachinery/pkg/runtime"
2829 "k8s.io/client-go/rest"
2930 "k8s.io/client-go/tools/record"
3031 "sigs.k8s.io/controller-runtime/pkg/client"
3132 "sigs.k8s.io/controller-runtime/pkg/log"
3233
34+ "github.com/SAP/metrics-operator/internal/clientoptl"
3335 "github.com/SAP/metrics-operator/internal/common"
3436 "github.com/SAP/metrics-operator/internal/config"
3537 "github.com/SAP/metrics-operator/internal/orchestrator"
@@ -57,6 +59,22 @@ func (r *ManagedMetricReconciler) getRestConfig() *rest.Config {
5759 return r .inRestConfig
5860}
5961
62+ func (r * ManagedMetricReconciler ) scheduleNextReconciliation (metric * v1alpha1.ManagedMetric ) (ctrl.Result , error ) {
63+ elapsed := time .Since (metric .Status .Observation .Timestamp .Time )
64+ return ctrl.Result {
65+ Requeue : true ,
66+ RequeueAfter : metric .Spec .Interval .Duration - elapsed ,
67+ }, nil
68+ }
69+
70+ func (r * ManagedMetricReconciler ) shouldReconcile (metric * v1alpha1.ManagedMetric ) bool {
71+ if metric .Status .Observation .Timestamp .Time .IsZero () {
72+ return true
73+ }
74+ elapsed := time .Since (metric .Status .Observation .Timestamp .Time )
75+ return elapsed >= metric .Spec .Interval .Duration
76+ }
77+
6078// ManagedMetricReconciler reconciles a ManagedMetric object
6179type ManagedMetricReconciler struct {
6280 inClient client.Client
@@ -96,6 +114,11 @@ func (r *ManagedMetricReconciler) Reconcile(ctx context.Context, req ctrl.Reques
96114 return ctrl.Result {RequeueAfter : RequeueAfterError }, errLoad
97115 }
98116
117+ // Check if enough time has passed since the last reconciliation
118+ if ! r .shouldReconcile (& metric ) {
119+ return r .scheduleNextReconciliation (& metric )
120+ }
121+
99122 /*
100123 1.1 Get the Secret that holds the Dynatrace credentials
101124 */
@@ -116,10 +139,34 @@ func (r *ManagedMetricReconciler) Reconcile(ctx context.Context, req ctrl.Reques
116139 return ctrl.Result {RequeueAfter : RequeueAfterError }, err
117140 }
118141
142+ /*
143+ 1.3 Create OTel metric client and gauge metric
144+ */
145+ metricClient , errCli := clientoptl .NewMetricClient (ctx , credentials .Host , credentials .Path , credentials .Token )
146+ if errCli != nil {
147+ l .Error (errCli , fmt .Sprintf ("managed metric '%s' re-queued for execution in %v minutes\n " , metric .Spec .Name , RequeueAfterError ))
148+ return ctrl.Result {RequeueAfter : RequeueAfterError }, errCli
149+ }
150+
151+ defer func () {
152+ if err := metricClient .Close (ctx ); err != nil {
153+ l .Error (err , "Failed to close metric client during managed metric reconciliation" , "metric" , metric .Name )
154+ }
155+ }()
156+
157+ // Set meter name for managed metrics
158+ metricClient .SetMeter ("managed" )
159+
160+ gaugeMetric , errGauge := metricClient .NewMetric (metric .Name )
161+ if errGauge != nil {
162+ l .Error (errGauge , fmt .Sprintf ("managed metric '%s' re-queued for execution in %v minutes\n " , metric .Spec .Name , RequeueAfterError ))
163+ return ctrl.Result {RequeueAfter : RequeueAfterError }, errGauge
164+ }
165+
119166 /*
120167 2. Create a new orchestrator
121168 */
122- orchestrator , errOrch := orchestrator .NewOrchestrator (credentials , queryConfig ).WithManaged (metric )
169+ orchestrator , errOrch := orchestrator .NewOrchestrator (credentials , queryConfig ).WithManaged (metric , gaugeMetric )
123170 if errOrch != nil {
124171 l .Error (errOrch , "unable to create managed metric orchestrator monitor" )
125172 r .Recorder .Event (& metric , "Warning" , "OrchestratorCreation" , "unable to create orchestrator" )
@@ -133,6 +180,17 @@ func (r *ManagedMetricReconciler) Reconcile(ctx context.Context, req ctrl.Reques
133180 return ctrl.Result {RequeueAfter : RequeueAfterError }, errMon
134181 }
135182
183+ /*
184+ 2.1 Export metrics to data sink
185+ */
186+ errExport := metricClient .ExportMetrics (ctx )
187+ if errExport != nil {
188+ metric .Status .Ready = v1alpha1 .StatusFalse
189+ l .Error (errExport , fmt .Sprintf ("managed metric '%s' re-queued for execution in %v minutes\n " , metric .Spec .Name , RequeueAfterError ))
190+ } else {
191+ metric .Status .Ready = v1alpha1 .StatusTrue
192+ }
193+
136194 /*
137195 3. Update the status of the metric with conditions and phase
138196 */
@@ -149,11 +207,16 @@ func (r *ManagedMetricReconciler) Reconcile(ctx context.Context, req ctrl.Reques
149207 r .Recorder .Event (& metric , "Normal" , "MetricPending" , result .Message )
150208 }
151209
152- metric .Status .Ready = v1alpha1 .StatusFalse
153- if result .Phase == v1alpha1 .PhaseActive {
154- metric .Status .Ready = v1alpha1 .StatusTrue
210+ // Override Ready status if export failed
211+ if errExport != nil {
212+ metric .Status .Ready = v1alpha1 .StatusFalse
213+ }
214+
215+ // Update the observation timestamp to track when this reconciliation happened
216+ metric .Status .Observation = v1alpha1.ManagedObservation {
217+ Timestamp : metav1 .Now (),
218+ Resources : result .Observation .GetValue (),
155219 }
156- metric .Status .Observation = v1alpha1.ManagedObservation {Timestamp : result .Observation .GetTimestamp (), Resources : result .Observation .GetValue ()}
157220
158221 // conditions are not persisted until the status is updated
159222 errUp := r .inClient .Status ().Update (ctx , & metric )
@@ -166,13 +229,13 @@ func (r *ManagedMetricReconciler) Reconcile(ctx context.Context, req ctrl.Reques
166229 4. Requeue the metric after the frequency or after 2 minutes if an error occurred
167230 */
168231 var requeueTime time.Duration
169- if result .Error != nil {
232+ if result .Error != nil || errExport != nil {
170233 requeueTime = RequeueAfterError
171234 } else {
172235 requeueTime = metric .Spec .Interval .Duration
173236 }
174237
175- l .Info (fmt .Sprintf ("managed metric '%s' re-queued for execution in %v minutes \n " , metric .Spec .Name , requeueTime ))
238+ l .Info (fmt .Sprintf ("managed metric '%s' re-queued for execution in %v\n " , metric .Spec .Name , requeueTime ))
176239
177240 return ctrl.Result {
178241 Requeue : true ,
0 commit comments