@@ -6,10 +6,12 @@ package controllers
66
77import (
88 "context"
9+ "fmt"
910 "strings"
1011 "time"
1112
1213 wsk8s "github.com/gitpod-io/gitpod/common-go/kubernetes"
14+ "github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/activity"
1315 "github.com/gitpod-io/gitpod/ws-manager-mk2/pkg/maintenance"
1416 workspacev1 "github.com/gitpod-io/gitpod/ws-manager/api/crd/v1"
1517 "github.com/go-logr/logr"
@@ -33,6 +35,7 @@ const (
3335 workspaceRestoresTotal string = "workspace_restores_total"
3436 workspaceRestoresFailureTotal string = "workspace_restores_failure_total"
3537 workspaceNodeUtilization string = "workspace_node_utilization"
38+ workspaceActivityTotal string = "workspace_activity_total"
3639)
3740
3841type StopReason string
@@ -65,6 +68,8 @@ type controllerMetrics struct {
6568
6669 workspaceNodeUtilization * nodeUtilizationVec
6770
71+ workspaceActivityTotal * workspaceActivityVec
72+
6873 // used to prevent recording metrics multiple times
6974 cache * lru.Cache
7075}
@@ -144,6 +149,7 @@ func newControllerMetrics(r *WorkspaceReconciler) (*controllerMetrics, error) {
144149 workspacePhases : newPhaseTotalVec (r ),
145150 timeoutSettings : newTimeoutSettingsVec (r ),
146151 workspaceNodeUtilization : newNodeUtilizationVec (r ),
152+ workspaceActivityTotal : newWorkspaceActivityVec (r ),
147153 cache : cache ,
148154 }, nil
149155}
@@ -328,6 +334,7 @@ func (m *controllerMetrics) Describe(ch chan<- *prometheus.Desc) {
328334 m .workspacePhases .Describe (ch )
329335 m .timeoutSettings .Describe (ch )
330336 m .workspaceNodeUtilization .Describe (ch )
337+ m .workspaceActivityTotal .Describe (ch )
331338}
332339
333340// Collect implements Collector.
@@ -347,6 +354,7 @@ func (m *controllerMetrics) Collect(ch chan<- prometheus.Metric) {
347354 m .workspacePhases .Collect (ch )
348355 m .timeoutSettings .Collect (ch )
349356 m .workspaceNodeUtilization .Collect (ch )
357+ m .workspaceActivityTotal .Collect (ch )
350358}
351359
352360// phaseTotalVec returns a gauge vector counting the workspaces per phase
@@ -612,3 +620,76 @@ func (n *nodeUtilizationVec) Collect(ch chan<- prometheus.Metric) {
612620 }
613621 }
614622}
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