@@ -2,9 +2,46 @@ package main
22
33import (
44 "context"
5+ "flag"
6+ "fmt"
7+ "net/http"
8+ "os"
59 "time"
610
11+ "github.com/kubernetes-csi/csi-lib-utils/metrics"
12+ lcoorV1 "k8s.io/api/coordination/v1"
13+ "k8s.io/apimachinery/pkg/util/wait"
14+ "k8s.io/client-go/informers"
15+ "k8s.io/client-go/kubernetes"
16+ "k8s.io/client-go/rest"
17+ "k8s.io/client-go/tools/cache"
18+ "k8s.io/client-go/tools/clientcmd"
19+ "k8s.io/client-go/util/workqueue"
20+ "k8s.io/klog/v2"
21+
722 lcsi "github.com/vngcloud/vngcloud-csi-volume-modifier/pkg/client"
23+ lcontroller "github.com/vngcloud/vngcloud-csi-volume-modifier/pkg/controller"
24+ lmodifier "github.com/vngcloud/vngcloud-csi-volume-modifier/pkg/modifier"
25+ )
26+
27+ // _____________________________________________________________________________________________________________________CONFIGURATION VARIABLES
28+
29+ var (
30+ resyncPeriod = flag .Duration ("resync-period" , time .Minute * 10 , "Resync period for cache" )
31+ workers = flag .Int ("workers" , 10 , "Concurrency to process multiple modification requests" )
32+ showVersion = flag .Bool ("version" , false , "Show version" )
33+ httpEndpoint = flag .String ("http-endpoint" , "" , "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string, which means the server is disabled. Only one of `--metrics-address` and `--http-endpoint` can be set." )
34+ clientConfigUrl = flag .String ("client-config-url" , "" , "URL to build a client config from. Either this or kubeconfig needs to be set if the provisioner is being run out of cluster." )
35+ kubeConfig = flag .String ("kubeconfig" , "" , "Absolute path to the kubeconfig" )
36+ kubeAPIQPS = flag .Float64 ("kube-api-qps" , 5 , "QPS to use while communicating with the kubernetes apiserver. Defaults to 5.0." )
37+ kubeAPIBurst = flag .Int ("kube-api-burst" , 10 , "Burst to use while communicating with the kubernetes apiserver. Defaults to 10." )
38+ csiAddress = flag .String ("csi-address" , "/run/csi/socket" , "Address of the CSI driver socket." )
39+ timeout = flag .Duration ("timeout" , 10 * time .Second , "Timeout for waiting for CSI driver socket." )
40+ metricsPath = flag .String ("metrics-path" , "/metrics" , "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`." )
41+ retryIntervalStart = flag .Duration ("retry-interval-start" , time .Second , "Initial retry interval of failed volume modification. It exponentially increases with each failure, up to retry-interval-max." )
42+ retryIntervalMax = flag .Duration ("retry-interval-max" , 5 * time .Minute , "Maximum retry interval of failed volume modification." )
43+
44+ version = "<unknown>"
845)
946
1047// _____________________________________________________________________________________________________________________PRIVATE METHODS
@@ -14,3 +51,160 @@ func getDriverName(pclient lcsi.IClient, ptimeout time.Duration) (string, error)
1451 defer cancel ()
1552 return pclient .GetDriverName (ctx )
1653}
54+
55+ func leaseHandler (ppodName string , mc lcontroller.IModifyController , leaseChannel chan * lcoorV1.Lease ) {
56+ var cancel context.CancelFunc = nil
57+
58+ klog .InfoS ("leaseHandler: Looking for external-resizer lease holder" )
59+
60+ timer := time .NewTimer (* resyncPeriod )
61+ defer timer .Stop ()
62+
63+ for {
64+ select {
65+ case lease , ok := <- leaseChannel :
66+ if ! ok {
67+ if cancel != nil {
68+ cancel ()
69+ }
70+ return
71+ }
72+ currentLeader := * lease .Spec .HolderIdentity
73+ klog .V (6 ).InfoS ("leaseHandler: Lease updated" , "currentLeader" , currentLeader , "podName" , ppodName )
74+
75+ if currentLeader == ppodName && cancel == nil {
76+ var ctx context.Context
77+ ctx , cancel = context .WithCancel (context .Background ())
78+ klog .InfoS ("leaseHandler: Starting ModifyController" , "podName" , ppodName , "currentLeader" , currentLeader )
79+ go mc .Run (* workers , ctx )
80+ } else if currentLeader != ppodName && cancel != nil {
81+ klog .InfoS ("leaseHandler: Stopping ModifyController" , "podName" , ppodName , "currentLeader" , currentLeader )
82+ cancel ()
83+ cancel = nil
84+ }
85+
86+ if ! timer .Stop () {
87+ <- timer .C
88+ }
89+ timer .Reset (* resyncPeriod )
90+
91+ case <- timer .C :
92+ if cancel != nil {
93+ cancel ()
94+ }
95+ klog .Fatalf ("leaseHandler: No external-resizer lease update received within timeout period. Timeout: %v" , * resyncPeriod )
96+ }
97+ }
98+ }
99+
100+ func main () {
101+ klog .InitFlags (nil )
102+ flag .Set ("logtostderr" , "true" )
103+ flag .Parse ()
104+
105+ if * showVersion {
106+ fmt .Println (os .Args [0 ], version )
107+ os .Exit (0 )
108+ }
109+ klog .Infof ("Version : %s" , version )
110+ klog .InfoS ("Leader election must be enabled in the external-resizer CSI sidecar" )
111+
112+ podName := os .Getenv ("POD_NAME" )
113+ if podName == "" {
114+ klog .Fatal ("POD_NAME environment variable is not set" )
115+ }
116+ podNamespace := os .Getenv ("POD_NAMESPACE" )
117+ if podNamespace == "" {
118+ klog .Fatal ("POD_NAMESPACE environment variable is not set" )
119+ }
120+
121+ addr := * httpEndpoint
122+ var config * rest.Config
123+ var err error
124+ if * clientConfigUrl != "" || * kubeConfig != "" {
125+ config , err = clientcmd .BuildConfigFromFlags (* clientConfigUrl , * kubeConfig )
126+ } else {
127+ config , err = rest .InClusterConfig ()
128+ }
129+ if err != nil {
130+ klog .Fatal (err .Error ())
131+ }
132+
133+ config .QPS = float32 (* kubeAPIQPS )
134+ config .Burst = * kubeAPIBurst
135+
136+ kubeClient , err := kubernetes .NewForConfig (config )
137+ if err != nil {
138+ klog .Fatal (err .Error ())
139+ }
140+
141+ informerFactory := informers .NewSharedInformerFactory (kubeClient , * resyncPeriod )
142+ mux := http .NewServeMux ()
143+ metricsManager := metrics .NewCSIMetricsManager ("" /* driverName */ )
144+ csiClient , err := lcsi .New (* csiAddress , * timeout , metricsManager )
145+ if err != nil {
146+ klog .Fatal (err .Error ())
147+ }
148+ if err := csiClient .SupportsVolumeModification (context .TODO ()); err != nil {
149+ klog .Fatalf ("CSI driver does not support volume modification: %v" , err )
150+ }
151+
152+ driverName , err := getDriverName (csiClient , * timeout )
153+ if err != nil {
154+ klog .Fatal (fmt .Errorf ("get driver name failed: %v" , err ))
155+ }
156+ klog .V (2 ).Infof ("CSI driver name: %q" , driverName )
157+
158+ csiModifier , err := lmodifier .NewFromClient (
159+ driverName ,
160+ csiClient ,
161+ kubeClient ,
162+ * timeout ,
163+ )
164+ if err != nil {
165+ klog .Fatal (err .Error ())
166+ }
167+
168+ if addr != "" {
169+ metricsManager .RegisterToServer (mux , * metricsPath )
170+ metricsManager .SetDriverName (driverName )
171+ go func () {
172+ klog .Infof ("ServeMux listening at %q" , addr )
173+ err := http .ListenAndServe (addr , mux )
174+ if err != nil {
175+ klog .Fatalf ("Failed to start HTTP server at specified address (%q) and metrics path (%q): %s" , addr , * metricsPath , err )
176+ }
177+ }()
178+ }
179+
180+ modifierName := csiModifier .Name ()
181+ mc := lcontroller .NewModifyController (
182+ modifierName ,
183+ csiModifier ,
184+ kubeClient ,
185+ * resyncPeriod ,
186+ informerFactory ,
187+ workqueue .NewItemExponentialFailureRateLimiter (* retryIntervalStart , * retryIntervalMax ),
188+ true , /* retryFailure */
189+ )
190+ leaseChannel := make (chan * lcoorV1.Lease )
191+ go leaseHandler (podName , mc , leaseChannel )
192+
193+ informerFactoryLeases := informers .NewSharedInformerFactoryWithOptions (kubeClient , * resyncPeriod , informers .WithNamespace (podNamespace ))
194+ leaseInformer := informerFactoryLeases .Coordination ().V1 ().Leases ().Informer ()
195+ leaseInformer .AddEventHandler (cache.ResourceEventHandlerFuncs {
196+ UpdateFunc : func (oldObj , newObj interface {}) {
197+ lease , ok := newObj .(* lcoorV1.Lease )
198+ if ! ok {
199+ klog .ErrorS (nil , "Failed to process object, expected it to be a Lease" , "obj" , newObj )
200+ return
201+ }
202+ if lease .Name == "external-resizer-bs-csi-vngcloud-vn" {
203+ leaseChannel <- lease
204+ }
205+ },
206+ })
207+ informerFactory .Start (wait .NeverStop )
208+ informerFactoryLeases .Start (wait .NeverStop )
209+ leaseInformer .Run (wait .NeverStop )
210+ }
0 commit comments