@@ -6,10 +6,12 @@ package controllers
6
6
7
7
import (
8
8
"context"
9
+ "fmt"
9
10
"strings"
10
11
"time"
11
12
12
13
wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
14
+ "github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/activity"
13
15
"github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/maintenance"
14
16
workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
15
17
"github.com/go-logr/logr"
@@ -33,6 +35,7 @@ const (
33
35
workspaceRestoresTotal string = "workspace_restores_total"
34
36
workspaceRestoresFailureTotal string = "workspace_restores_failure_total"
35
37
workspaceNodeUtilization string = "workspace_node_utilization"
38
+ workspaceActivityTotal string = "workspace_activity_total"
36
39
)
37
40
38
41
type StopReason string
@@ -65,6 +68,8 @@ type controllerMetrics struct {
65
68
66
69
workspaceNodeUtilization * nodeUtilizationVec
67
70
71
+ workspaceActivityTotal * workspaceActivityVec
72
+
68
73
// used to prevent recording metrics multiple times
69
74
cache * lru.Cache
70
75
}
@@ -144,6 +149,7 @@ func newControllerMetrics(r *WorkspaceReconciler) (*controllerMetrics, error) {
144
149
workspacePhases : newPhaseTotalVec (r ),
145
150
timeoutSettings : newTimeoutSettingsVec (r ),
146
151
workspaceNodeUtilization : newNodeUtilizationVec (r ),
152
+ workspaceActivityTotal : newWorkspaceActivityVec (r ),
147
153
cache : cache ,
148
154
}, nil
149
155
}
@@ -328,6 +334,7 @@ func (m *controllerMetrics) Describe(ch chan<- *prometheus.Desc) {
328
334
m .workspacePhases .Describe (ch )
329
335
m .timeoutSettings .Describe (ch )
330
336
m .workspaceNodeUtilization .Describe (ch )
337
+ m .workspaceActivityTotal .Describe (ch )
331
338
}
332
339
333
340
// Collect implements Collector.
@@ -347,6 +354,7 @@ func (m *controllerMetrics) Collect(ch chan<- prometheus.Metric) {
347
354
m .workspacePhases .Collect (ch )
348
355
m .timeoutSettings .Collect (ch )
349
356
m .workspaceNodeUtilization .Collect (ch )
357
+ m .workspaceActivityTotal .Collect (ch )
350
358
}
351
359
352
360
// phaseTotalVec returns a gauge vector counting the workspaces per phase
@@ -612,3 +620,76 @@ func (n *nodeUtilizationVec) Collect(ch chan<- prometheus.Metric) {
612
620
}
613
621
}
614
622
}
623
+
624
+ type workspaceActivityVec struct {
625
+ name string
626
+ desc * prometheus.Desc
627
+ reconciler * WorkspaceReconciler
628
+ }
629
+
630
+ func newWorkspaceActivityVec (r * WorkspaceReconciler ) * workspaceActivityVec {
631
+ name := prometheus .BuildFQName (metricsNamespace , metricsWorkspaceSubsystem , workspaceActivityTotal )
632
+ desc := prometheus .NewDesc (
633
+ name ,
634
+ "total number of active workspaces" ,
635
+ []string {"active" },
636
+ prometheus .Labels (map [string ]string {}),
637
+ )
638
+ return & workspaceActivityVec {
639
+ name : name ,
640
+ desc : desc ,
641
+ reconciler : r ,
642
+ }
643
+ }
644
+
645
+ // Describe implements Collector. It will send exactly one Desc to the provided channel.
646
+ func (wav * workspaceActivityVec ) Describe (ch chan <- * prometheus.Desc ) {
647
+ ch <- wav .desc
648
+ }
649
+
650
+ func (wav * workspaceActivityVec ) Collect (ch chan <- prometheus.Metric ) {
651
+ ctx , cancel := context .WithTimeout (context .Background (), kubernetesOperationTimeout )
652
+ defer cancel ()
653
+
654
+ active , notActive , err := wav .getWorkspaceActivityCounts ()
655
+ if err != nil {
656
+ log .FromContext (ctx ).Error (err , fmt .Sprintf ("cannot determine active/inactive counts - %s will be inaccurate" , wav .name ))
657
+ return
658
+ }
659
+
660
+ activeMetrics , err := prometheus .NewConstMetric (wav .desc , prometheus .GaugeValue , float64 (active ), "true" )
661
+ if err != nil {
662
+ log .FromContext (ctx ).Error (err , "cannot create wrokspace activity metric" , "active" , "true" )
663
+ return
664
+ }
665
+ notActiveMetrics , err := prometheus .NewConstMetric (wav .desc , prometheus .GaugeValue , float64 (notActive ), "false" )
666
+ if err != nil {
667
+ log .FromContext (ctx ).Error (err , "cannot create wrokspace activity metric" , "active" , "false" )
668
+ return
669
+ }
670
+
671
+ ch <- activeMetrics
672
+ ch <- notActiveMetrics
673
+ }
674
+
675
+ func (wav * workspaceActivityVec ) getWorkspaceActivityCounts () (active , notActive int , err error ) {
676
+ var workspaces workspacev1.WorkspaceList
677
+ if err = wav .reconciler .List (context .Background (), & workspaces , client .InNamespace (wav .reconciler .Config .Namespace )); err != nil {
678
+ return 0 , 0 , err
679
+ }
680
+
681
+ for _ , ws := range workspaces .Items {
682
+ if ws .Spec .Type != workspacev1 .WorkspaceTypeRegular {
683
+ continue
684
+ }
685
+
686
+ hasActivity := activity .Last (& ws ) != nil
687
+ if hasActivity {
688
+ active ++
689
+ } else {
690
+ notActive ++
691
+ }
692
+ }
693
+
694
+ return
695
+ }
0 commit comments