@@ -18,22 +18,29 @@ package controller
18
18
19
19
import (
20
20
"fmt"
21
+ "net"
22
+ "net/http"
21
23
"os/exec"
22
24
"reflect"
25
+ "strconv"
23
26
"strings"
24
27
"sync"
25
28
"time"
26
29
27
30
"github.com/golang/glog"
31
+ "github.com/kubernetes-incubator/external-storage/lib/controller/metrics"
28
32
"github.com/kubernetes-incubator/external-storage/lib/leaderelection"
29
33
rl "github.com/kubernetes-incubator/external-storage/lib/leaderelection/resourcelock"
34
+ "github.com/prometheus/client_golang/prometheus"
35
+ "github.com/prometheus/client_golang/prometheus/promhttp"
30
36
"k8s.io/api/core/v1"
31
37
storage "k8s.io/api/storage/v1"
32
38
storagebeta "k8s.io/api/storage/v1beta1"
33
39
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34
40
"k8s.io/apimachinery/pkg/runtime"
35
41
"k8s.io/apimachinery/pkg/types"
36
42
"k8s.io/apimachinery/pkg/util/uuid"
43
+ "k8s.io/apimachinery/pkg/util/wait"
37
44
"k8s.io/apimachinery/pkg/watch"
38
45
"k8s.io/client-go/kubernetes"
39
46
"k8s.io/client-go/kubernetes/scheme"
@@ -122,6 +129,13 @@ type ProvisionController struct {
122
129
failedProvisionStats , failedDeleteStats map [types.UID ]int
123
130
failedProvisionStatsMutex , failedDeleteStatsMutex * sync.Mutex
124
131
132
+ // The port for metrics server to serve on.
133
+ metricsPort int32
134
+ // The IP address for metrics server to serve on.
135
+ metricsAddress string
136
+ // The path of metrics endpoint path.
137
+ metricsPath string
138
+
125
139
// Parameters of leaderelection.LeaderElectionConfig. Leader election is for
126
140
// when multiple controllers are running: they race to lock (lead) every PVC
127
141
// so that only one calls Provision for it (saving API calls, CPU cycles...)
@@ -156,6 +170,12 @@ const (
156
170
DefaultRetryPeriod = 2 * time .Second
157
171
// DefaultTermLimit is used when option function TermLimit is omitted
158
172
DefaultTermLimit = 30 * time .Second
173
+ // DefaultMetricsPort is used when option function MetricsPort is omitted
174
+ DefaultMetricsPort = 0
175
+ // DefaultMetricsAddress is used when option function MetricsAddress is omitted
176
+ DefaultMetricsAddress = "0.0.0.0"
177
+ // DefaultMetricsPath is used when option function MetricsPath is omitted
178
+ DefaultMetricsPath = "/metrics"
159
179
)
160
180
161
181
var errRuntime = fmt .Errorf ("cannot call option functions after controller has Run" )
@@ -316,6 +336,39 @@ func ClassesInformer(informer cache.SharedInformer) func(*ProvisionController) e
316
336
}
317
337
}
318
338
339
+ // MetricsPort sets the port that metrics server serves on. Default: 0, set to non-zero to enable.
340
+ func MetricsPort (metricsPort int32 ) func (* ProvisionController ) error {
341
+ return func (c * ProvisionController ) error {
342
+ if c .HasRun () {
343
+ return errRuntime
344
+ }
345
+ c .metricsPort = metricsPort
346
+ return nil
347
+ }
348
+ }
349
+
350
+ // MetricsAddress sets the ip address that metrics serve serves on.
351
+ func MetricsAddress (metricsAddress string ) func (* ProvisionController ) error {
352
+ return func (c * ProvisionController ) error {
353
+ if c .HasRun () {
354
+ return errRuntime
355
+ }
356
+ c .metricsAddress = metricsAddress
357
+ return nil
358
+ }
359
+ }
360
+
361
+ // MetricsPath sets the endpoint path of metrics server.
362
+ func MetricsPath (metricsPath string ) func (* ProvisionController ) error {
363
+ return func (c * ProvisionController ) error {
364
+ if c .HasRun () {
365
+ return errRuntime
366
+ }
367
+ c .metricsPath = metricsPath
368
+ return nil
369
+ }
370
+ }
371
+
319
372
// NewProvisionController creates a new provision controller using
320
373
// the given configuration parameters and with private (non-shared) informers.
321
374
func NewProvisionController (
@@ -360,6 +413,9 @@ func NewProvisionController(
360
413
renewDeadline : DefaultRenewDeadline ,
361
414
retryPeriod : DefaultRetryPeriod ,
362
415
termLimit : DefaultTermLimit ,
416
+ metricsPort : DefaultMetricsPort ,
417
+ metricsAddress : DefaultMetricsAddress ,
418
+ metricsPath : DefaultMetricsPath ,
363
419
leaderElectors : make (map [types.UID ]* leaderelection.LeaderElector ),
364
420
leaderElectorsMutex : & sync.Mutex {},
365
421
hasRun : false ,
@@ -493,6 +549,25 @@ func (ctrl *ProvisionController) Run(stopCh <-chan struct{}) {
493
549
ctrl .hasRunLock .Lock ()
494
550
ctrl .hasRun = true
495
551
ctrl .hasRunLock .Unlock ()
552
+ if ctrl .metricsPort > 0 {
553
+ prometheus .MustRegister ([]prometheus.Collector {
554
+ metrics .PersistentVolumeClaimProvisionTotal ,
555
+ metrics .PersistentVolumeClaimProvisionFailedTotal ,
556
+ metrics .PersistentVolumeClaimProvisionDurationSeconds ,
557
+ metrics .PersistentVolumeDeleteTotal ,
558
+ metrics .PersistentVolumeDeleteFailedTotal ,
559
+ metrics .PersistentVolumeDeleteDurationSeconds ,
560
+ }... )
561
+ http .Handle (ctrl .metricsPath , promhttp .Handler ())
562
+ address := net .JoinHostPort (ctrl .metricsAddress , strconv .FormatInt (int64 (ctrl .metricsPort ), 10 ))
563
+ glog .Infof ("Starting metrics server at %s\n " , address )
564
+ go wait .Forever (func () {
565
+ err := http .ListenAndServe (address , nil )
566
+ if err != nil {
567
+ glog .Errorf ("Failed to listen on %s: %v" , address , err )
568
+ }
569
+ }, 5 * time .Second )
570
+ }
496
571
go ctrl .claimController .Run (stopCh )
497
572
go ctrl .volumeController .Run (stopCh )
498
573
go ctrl .classController .Run (stopCh )
@@ -536,8 +611,9 @@ func (ctrl *ProvisionController) addClaim(obj interface{}) {
536
611
if ok && le .IsLeader () {
537
612
opName := fmt .Sprintf ("provision-%s[%s]" , claimToClaimKey (claim ), string (claim .UID ))
538
613
ctrl .scheduleOperation (opName , func () error {
614
+ startTime := time .Now ()
539
615
err := ctrl .provisionClaimOperation (claim )
540
- ctrl .updateProvisionStats (claim , err )
616
+ ctrl .updateProvisionStats (claim , err , startTime )
541
617
return err
542
618
})
543
619
} else {
@@ -596,8 +672,9 @@ func (ctrl *ProvisionController) updateVolume(oldObj, newObj interface{}) {
596
672
if ctrl .shouldDelete (volume ) {
597
673
opName := fmt .Sprintf ("delete-%s[%s]" , volume .Name , string (volume .UID ))
598
674
ctrl .scheduleOperation (opName , func () error {
675
+ startTime := time .Now ()
599
676
err := ctrl .deleteVolumeOperation (volume )
600
- ctrl .updateDeleteStats (volume , err )
677
+ ctrl .updateDeleteStats (volume , err , startTime )
601
678
return err
602
679
})
603
680
}
@@ -741,8 +818,9 @@ func (ctrl *ProvisionController) lockProvisionClaimOperation(claim *v1.Persisten
741
818
OnStartedLeading : func (_ <- chan struct {}) {
742
819
opName := fmt .Sprintf ("provision-%s[%s]" , claimToClaimKey (claim ), string (claim .UID ))
743
820
ctrl .scheduleOperation (opName , func () error {
821
+ startTime := time .Now ()
744
822
err := ctrl .provisionClaimOperation (claim )
745
- ctrl .updateProvisionStats (claim , err )
823
+ ctrl .updateProvisionStats (claim , err , startTime )
746
824
return err
747
825
})
748
826
},
@@ -785,10 +863,21 @@ func (ctrl *ProvisionController) lockProvisionClaimOperation(claim *v1.Persisten
785
863
ctrl .leaderElectorsMutex .Unlock ()
786
864
}
787
865
788
- func (ctrl * ProvisionController ) updateProvisionStats (claim * v1.PersistentVolumeClaim , err error ) {
866
+ func (ctrl * ProvisionController ) updateProvisionStats (claim * v1.PersistentVolumeClaim , err error , startTime time. Time ) {
789
867
ctrl .failedProvisionStatsMutex .Lock ()
790
868
defer ctrl .failedProvisionStatsMutex .Unlock ()
791
869
870
+ class := ""
871
+ if claim .Spec .StorageClassName != nil {
872
+ class = * claim .Spec .StorageClassName
873
+ }
874
+ if err != nil {
875
+ metrics .PersistentVolumeClaimProvisionFailedTotal .WithLabelValues (class ).Inc ()
876
+ } else {
877
+ metrics .PersistentVolumeClaimProvisionDurationSeconds .WithLabelValues (class ).Observe (time .Since (startTime ).Seconds ())
878
+ metrics .PersistentVolumeClaimProvisionTotal .WithLabelValues (class ).Inc ()
879
+ }
880
+
792
881
// Do not record the failed claim info when failedProvisionThreshold is not set
793
882
if ctrl .failedProvisionThreshold <= 0 {
794
883
return
@@ -806,10 +895,18 @@ func (ctrl *ProvisionController) updateProvisionStats(claim *v1.PersistentVolume
806
895
}
807
896
}
808
897
809
- func (ctrl * ProvisionController ) updateDeleteStats (volume * v1.PersistentVolume , err error ) {
898
+ func (ctrl * ProvisionController ) updateDeleteStats (volume * v1.PersistentVolume , err error , startTime time. Time ) {
810
899
ctrl .failedDeleteStatsMutex .Lock ()
811
900
defer ctrl .failedDeleteStatsMutex .Unlock ()
812
901
902
+ class := volume .Spec .StorageClassName
903
+ if err != nil {
904
+ metrics .PersistentVolumeDeleteFailedTotal .WithLabelValues (class ).Inc ()
905
+ } else {
906
+ metrics .PersistentVolumeDeleteDurationSeconds .WithLabelValues (class ).Observe (time .Since (startTime ).Seconds ())
907
+ metrics .PersistentVolumeDeleteTotal .WithLabelValues (class ).Inc ()
908
+ }
909
+
813
910
// Do not record the failed volume info when failedDeleteThreshold is not set
814
911
if ctrl .failedDeleteThreshold <= 0 {
815
912
return
0 commit comments