Skip to content

Commit 51fe0aa

Browse files
committed
Make Pod informer registration optional
Changes: 1. Added support for feature gate flag parsing (Sample flag looks like "--feature-gates=VolumeInUseErrorHandler=true") 2. Register Pod informer only if feature is enabled.
1 parent 0df1aa7 commit 51fe0aa

File tree

5 files changed

+159
-26
lines changed

5 files changed

+159
-26
lines changed

cmd/csi-resizer/main.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,25 @@ package main
1818

1919
import (
2020
"context"
21-
"flag"
21+
goflag "flag"
2222
"fmt"
23-
"k8s.io/client-go/util/workqueue"
2423
"os"
2524
"time"
2625

26+
flag "github.com/spf13/pflag"
27+
"k8s.io/client-go/util/workqueue"
28+
2729
"github.com/kubernetes-csi/csi-lib-utils/leaderelection"
2830
"github.com/kubernetes-csi/external-resizer/pkg/controller"
2931
"github.com/kubernetes-csi/external-resizer/pkg/resizer"
3032
"github.com/kubernetes-csi/external-resizer/pkg/util"
3133

34+
"strings"
35+
3236
"k8s.io/apimachinery/pkg/util/wait"
37+
utilfeature "k8s.io/apiserver/pkg/util/feature"
3338
"k8s.io/client-go/informers"
39+
utilflag "k8s.io/component-base/cli/flag"
3440
"k8s.io/klog"
3541
)
3642

@@ -53,14 +59,22 @@ var (
5359
metricsAddress = flag.String("metrics-address", "", "The TCP network address where the prometheus metrics endpoint will listen (example: `:8080`). The default is empty string, which means metrics endpoint is disabled.")
5460
metricsPath = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
5561

56-
version = "unknown"
62+
version = "unknown"
63+
featureGates map[string]bool
5764
)
5865

5966
func main() {
6067
klog.InitFlags(nil)
6168
flag.Set("logtostderr", "true")
69+
flag.Var(utilflag.NewMapStringBool(&featureGates), "feature-gates", "A set of key=value pairs that describe feature gates. "+
70+
"Options are:\n"+strings.Join(utilfeature.DefaultFeatureGate.KnownFeatures(), "\n"))
71+
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
6272
flag.Parse()
6373

74+
if err := utilfeature.DefaultMutableFeatureGate.SetFromMap(featureGates); err != nil {
75+
klog.Fatal(err.Error())
76+
}
77+
6478
if *showVersion {
6579
fmt.Println(os.Args[0], version)
6680
os.Exit(0)

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ require (
66
github.com/container-storage-interface/spec v1.2.0
77
github.com/imdario/mergo v0.3.9 // indirect
88
github.com/kubernetes-csi/csi-lib-utils v0.7.0
9+
github.com/spf13/pflag v1.0.5
910
google.golang.org/grpc v1.28.0
1011
k8s.io/api v0.18.0
1112
k8s.io/apimachinery v0.18.0
13+
k8s.io/apiserver v0.18.4
1214
k8s.io/client-go v0.18.0
15+
k8s.io/component-base v0.18.0
1316
k8s.io/csi-translation-lib v0.18.0
1417
k8s.io/klog v1.0.0
1518
)

pkg/controller/controller.go

+33-22
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"time"
2323

24+
"github.com/kubernetes-csi/external-resizer/pkg/features"
2425
"github.com/kubernetes-csi/external-resizer/pkg/resizer"
2526
"github.com/kubernetes-csi/external-resizer/pkg/util"
2627
"google.golang.org/grpc/codes"
@@ -31,6 +32,7 @@ import (
3132
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3233
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3334
"k8s.io/apimachinery/pkg/util/wait"
35+
utilfeature "k8s.io/apiserver/pkg/util/feature"
3436
"k8s.io/client-go/informers"
3537
"k8s.io/client-go/kubernetes"
3638
"k8s.io/client-go/kubernetes/scheme"
@@ -76,10 +78,6 @@ func NewResizeController(
7678
pvcRateLimiter workqueue.RateLimiter) ResizeController {
7779
pvInformer := informerFactory.Core().V1().PersistentVolumes()
7880
pvcInformer := informerFactory.Core().V1().PersistentVolumeClaims()
79-
80-
// list pods so as we can identify PVC that are in-use
81-
podInformer := informerFactory.Core().V1().Pods()
82-
8381
eventBroadcaster := record.NewBroadcaster()
8482
eventBroadcaster.StartLogging(klog.Infof)
8583
eventBroadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: kubeClient.CoreV1().Events(v1.NamespaceAll)})
@@ -90,18 +88,16 @@ func NewResizeController(
9088
pvcRateLimiter, fmt.Sprintf("%s-pvc", name))
9189

9290
ctrl := &resizeController{
93-
name: name,
94-
resizer: resizer,
95-
kubeClient: kubeClient,
96-
pvLister: pvInformer.Lister(),
97-
pvSynced: pvInformer.Informer().HasSynced,
98-
pvcLister: pvcInformer.Lister(),
99-
pvcSynced: pvcInformer.Informer().HasSynced,
100-
podLister: podInformer.Lister(),
101-
podListerSynced: podInformer.Informer().HasSynced,
102-
claimQueue: claimQueue,
103-
eventRecorder: eventRecorder,
104-
usedPVCs: newUsedPVCStore(),
91+
name: name,
92+
resizer: resizer,
93+
kubeClient: kubeClient,
94+
pvLister: pvInformer.Lister(),
95+
pvSynced: pvInformer.Informer().HasSynced,
96+
pvcLister: pvcInformer.Lister(),
97+
pvcSynced: pvcInformer.Informer().HasSynced,
98+
claimQueue: claimQueue,
99+
eventRecorder: eventRecorder,
100+
usedPVCs: newUsedPVCStore(),
105101
}
106102

107103
// Add a resync period as the PVC's request size can be resized again when we handling
@@ -112,11 +108,18 @@ func NewResizeController(
112108
DeleteFunc: ctrl.deletePVC,
113109
}, resyncPeriod)
114110

115-
podInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{
116-
AddFunc: ctrl.addPod,
117-
DeleteFunc: ctrl.deletePod,
118-
UpdateFunc: ctrl.updatePod,
119-
}, resyncPeriod)
111+
if isVolumeInUserErrorHandlerEnabled() {
112+
// list pods so as we can identify PVC that are in-use
113+
klog.Infof("Register Pod informer for resizer %s", ctrl.name)
114+
podInformer := informerFactory.Core().V1().Pods()
115+
ctrl.podLister = podInformer.Lister()
116+
ctrl.podListerSynced = podInformer.Informer().HasSynced
117+
podInformer.Informer().AddEventHandlerWithResyncPeriod(cache.ResourceEventHandlerFuncs{
118+
AddFunc: ctrl.addPod,
119+
DeleteFunc: ctrl.deletePod,
120+
UpdateFunc: ctrl.updatePod,
121+
}, resyncPeriod)
122+
}
120123

121124
return ctrl
122125
}
@@ -235,8 +238,12 @@ func (ctrl *resizeController) Run(
235238
defer klog.Infof("Shutting down external resizer %s", ctrl.name)
236239

237240
stopCh := ctx.Done()
241+
informersSyncd := []cache.InformerSynced{ctrl.pvSynced, ctrl.pvcSynced}
242+
if isVolumeInUserErrorHandlerEnabled() {
243+
informersSyncd = append(informersSyncd, ctrl.podListerSynced)
244+
}
238245

239-
if !cache.WaitForCacheSync(stopCh, ctrl.pvSynced, ctrl.pvcSynced, ctrl.podListerSynced) {
246+
if !cache.WaitForCacheSync(stopCh, informersSyncd...) {
240247
klog.Errorf("Cannot sync pod, pv or pvc caches")
241248
return
242249
}
@@ -523,3 +530,7 @@ func inUseError(err error) bool {
523530
}
524531
return false
525532
}
533+
534+
func isVolumeInUserErrorHandlerEnabled() bool {
535+
return utilfeature.DefaultFeatureGate.Enabled(features.VolumeInUseErrorHandler)
536+
}

pkg/controller/controller_test.go

+70-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"testing"
66
"time"
77

8+
utilfeature "k8s.io/apiserver/pkg/util/feature"
89
"k8s.io/client-go/util/workqueue"
910

1011
"github.com/kubernetes-csi/csi-lib-utils/metrics"
@@ -21,9 +22,17 @@ import (
2122
"k8s.io/client-go/kubernetes/fake"
2223
)
2324

25+
func setupVolumeInUseErrorHandler(enable bool) {
26+
featureGates := map[string]bool{
27+
"VolumeInUseErrorHandler": enable,
28+
}
29+
utilfeature.DefaultMutableFeatureGate.SetFromMap(featureGates)
30+
}
31+
2432
func TestController(t *testing.T) {
2533
blockVolumeMode := v1.PersistentVolumeBlock
2634
fsVolumeMode := v1.PersistentVolumeFilesystem
35+
2736
for _, test := range []struct {
2837
Name string
2938
PVC *v1.PersistentVolumeClaim
@@ -37,7 +46,8 @@ func TestController(t *testing.T) {
3746
// is PVC being expanded in-use
3847
pvcInUse bool
3948
// does PVC being expanded has Failed Precondition errors
40-
pvcHasInUseErrors bool
49+
pvcHasInUseErrors bool
50+
disableVolumeInUseErrorHandler bool
4151
}{
4252
{
4353
Name: "Invalid key",
@@ -130,7 +140,66 @@ func TestController(t *testing.T) {
130140
pvcHasInUseErrors: true,
131141
pvcInUse: false,
132142
},
143+
// test cases with feat disabled.
144+
{
145+
Name: "Feat disabled, Resize PVC, no FS resize, pvc-inuse with failedprecondition",
146+
PVC: createPVC(2, 1),
147+
PV: createPV(1, "testPVC", defaultNS, "foobar", &fsVolumeMode),
148+
CreateObjects: true,
149+
CallCSIExpand: true,
150+
pvcHasInUseErrors: true,
151+
pvcInUse: true,
152+
disableVolumeInUseErrorHandler: true,
153+
},
154+
{
155+
Name: "Feat disabled, Resize PVC, no FS resize, pvc-inuse but no failedprecondition error",
156+
PVC: createPVC(2, 1),
157+
PV: createPV(1, "testPVC", defaultNS, "foobar", &fsVolumeMode),
158+
CreateObjects: true,
159+
CallCSIExpand: true,
160+
pvcHasInUseErrors: false,
161+
pvcInUse: true,
162+
disableVolumeInUseErrorHandler: true,
163+
},
164+
{
165+
Name: "Feat disabled, Resize PVC, no FS resize, pvc not in-use but has failedprecondition error",
166+
PVC: createPVC(2, 1),
167+
PV: createPV(1, "testPVC", defaultNS, "foobar", &fsVolumeMode),
168+
CreateObjects: true,
169+
CallCSIExpand: true,
170+
pvcHasInUseErrors: true,
171+
pvcInUse: false,
172+
disableVolumeInUseErrorHandler: true,
173+
},
174+
{
175+
Name: "Feat disabled, Block Resize PVC with FS resize",
176+
PVC: createPVC(2, 1),
177+
PV: createPV(1, "testPVC", defaultNS, "foobar", &blockVolumeMode),
178+
CreateObjects: true,
179+
NodeResize: true,
180+
CallCSIExpand: true,
181+
expectBlockVolume: true,
182+
disableVolumeInUseErrorHandler: true,
183+
},
184+
{
185+
Name: "Feat disabled, Resize PVC with FS resize",
186+
PVC: createPVC(2, 1),
187+
PV: createPV(1, "testPVC", defaultNS, "foobar", &fsVolumeMode),
188+
CreateObjects: true,
189+
NodeResize: true,
190+
CallCSIExpand: true,
191+
disableVolumeInUseErrorHandler: true,
192+
},
193+
{
194+
Name: "Feat disabled, Resize PVC, no FS resize",
195+
PVC: createPVC(2, 1),
196+
PV: createPV(1, "testPVC", defaultNS, "foobar", &fsVolumeMode),
197+
CreateObjects: true,
198+
CallCSIExpand: true,
199+
disableVolumeInUseErrorHandler: true,
200+
},
133201
} {
202+
setupVolumeInUseErrorHandler(!test.disableVolumeInUseErrorHandler)
134203
client := csi.NewMockClient("mock", test.NodeResize, true, true)
135204
driverName, _ := client.GetDriverName(context.TODO())
136205

pkg/features/features.go

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package features
18+
19+
import (
20+
"k8s.io/apiserver/pkg/util/feature"
21+
"k8s.io/component-base/featuregate"
22+
)
23+
24+
const (
25+
VolumeInUseErrorHandler featuregate.Feature = "VolumeInUseErrorHandler"
26+
)
27+
28+
func init() {
29+
feature.DefaultMutableFeatureGate.Add(defaultKubernetesFeatureGates)
30+
}
31+
32+
// defaultKubernetesFeatureGates consists of all known feature keys specific to external-resizer.
33+
// To add a new feature, define a key for it above and add it here.
34+
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
35+
VolumeInUseErrorHandler: {Default: false, PreRelease: featuregate.GA},
36+
}

0 commit comments

Comments
 (0)