diff --git a/cmd/manager/main.go b/cmd/manager/main.go index 21514d09c..88c7b3ebe 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -101,6 +101,7 @@ func main() { flag.BoolVar(&showVersion, "version", false, "Print Version details") flag.StringVar(&cfg.SchedulePrecedence, "schedule-precedence", "", "The order of precedence in which schedule of reclaimspace and keyrotation is considered. Possible values are sc-only") flag.BoolVar(&enableAuth, "enable-auth", true, "Enables TLS and adds bearer token to the headers (enabled by default)") + flag.IntVar(&cfg.MaxGroupPVC, "max-group-pvc", cfg.MaxGroupPVC, "Maximum number of PVCs allowed in a volume group") opts := zap.Options{ Development: true, TimeEncoder: zapcore.ISO8601TimeEncoder, @@ -244,9 +245,10 @@ func main() { } if err = (&replicationController.VolumeGroupReplicationReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - Recorder: mgr.GetEventRecorderFor("volumegroupreplication-controller"), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + Recorder: mgr.GetEventRecorderFor("volumegroupreplication-controller"), + MaxGroupPVCCount: cfg.MaxGroupPVC, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "VolumeGroupReplication") os.Exit(1) diff --git a/deploy/controller/csi-addons-config.yaml b/deploy/controller/csi-addons-config.yaml index 85246e346..618c1045f 100644 --- a/deploy/controller/csi-addons-config.yaml +++ b/deploy/controller/csi-addons-config.yaml @@ -8,3 +8,4 @@ metadata: data: "reclaim-space-timeout": "3m" "max-concurrent-reconciles": "100" + "max-group-pvcs": "100" diff --git a/docs/csi-addons-config.md b/docs/csi-addons-config.md index 4111c140c..2de2e734e 100644 --- a/docs/csi-addons-config.md +++ b/docs/csi-addons-config.md @@ -4,11 +4,14 @@ CSI-Addons Operator can consume configuration from a ConfigMap named `csi-addons in the same namespace as the operator. This enables configuration of the operator to persist across upgrades. The ConfigMap can support the following configuration options: -| Option | Default value | Description | -| --------------------------- | ------------- | --------------------------------------- | -| `reclaim-space-timeout` | `"3m"` | Timeout for reclaimspace operation | -| `max-concurrent-reconciles` | `"100"` | Maximum number of concurrent reconciles | +| Option | Default value | Description | +| --------------------------- | ------------- | ------------------------------------------------ | +| `reclaim-space-timeout` | `"3m"` | Timeout for reclaimspace operation | +| `max-concurrent-reconciles` | `"100"` | Maximum number of concurrent reconciles | +| `max-group-pvcs` | `"100"` | Maximum number of PVCs allowed in a volume group | [`csi-addons-config` ConfigMap](../deploy/controller/csi-addons-config.yaml) is provided as an example. > Note: The operator pod needs to be restarted for any change in configuration to take effect. +> +> Note: `max-group-pvcs` default value is set based on ceph's support/testing. User can tweak this value based on the supported count for their storage vendor. diff --git a/internal/controller/replication.storage/volumegroupreplication_controller.go b/internal/controller/replication.storage/volumegroupreplication_controller.go index 32f9da063..7c023d1af 100644 --- a/internal/controller/replication.storage/volumegroupreplication_controller.go +++ b/internal/controller/replication.storage/volumegroupreplication_controller.go @@ -58,10 +58,11 @@ const ( // VolumeGroupReplicationReconciler reconciles a VolumeGroupReplication object type VolumeGroupReplicationReconciler struct { client.Client - Scheme *runtime.Scheme - ctx context.Context - log logr.Logger - Recorder record.EventRecorder + Scheme *runtime.Scheme + ctx context.Context + log logr.Logger + Recorder record.EventRecorder + MaxGroupPVCCount int } //+kubebuilder:rbac:groups="",resources=events,verbs=create;patch @@ -162,9 +163,9 @@ func (r *VolumeGroupReplicationReconciler) Reconcile(ctx context.Context, req ct _ = r.setGroupReplicationFailure(instance, err) return reconcile.Result{}, err } - if len(pvcList) > 100 { - err = fmt.Errorf("more than 100 PVCs match the given selector") - r.log.Error(err, "only 100 PVCs are allowed for volume group replication") + if len(pvcList) > r.MaxGroupPVCCount { + err = fmt.Errorf("more than %q PVCs match the given selector", r.MaxGroupPVCCount) + r.log.Error(err, "only %q PVCs are allowed for volume group replication", r.MaxGroupPVCCount) _ = r.setGroupReplicationFailure(instance, err) return reconcile.Result{}, err } diff --git a/internal/controller/replication.storage/volumegroupreplication_test.go b/internal/controller/replication.storage/volumegroupreplication_test.go index f3366e1a6..cc15266d4 100644 --- a/internal/controller/replication.storage/volumegroupreplication_test.go +++ b/internal/controller/replication.storage/volumegroupreplication_test.go @@ -97,10 +97,11 @@ func createFakeVolumeGroupReplicationReconciler(t *testing.T, obj ...runtime.Obj reconcilerCtx := context.TODO() return VolumeGroupReplicationReconciler{ - Client: client, - Scheme: scheme, - log: logger, - ctx: reconcilerCtx, + Client: client, + Scheme: scheme, + log: logger, + ctx: reconcilerCtx, + MaxGroupPVCCount: 100, } } diff --git a/internal/util/config.go b/internal/util/config.go index 3e2be43aa..579b602d1 100644 --- a/internal/util/config.go +++ b/internal/util/config.go @@ -34,6 +34,7 @@ type Config struct { ReclaimSpaceTimeout time.Duration MaxConcurrentReconciles int SchedulePrecedence string + MaxGroupPVC int } const ( @@ -45,6 +46,8 @@ const ( defaultReclaimSpaceTimeout = time.Minute * 3 SchedulePrecedenceKey = "schedule-precedence" ScheduleSCOnly = "sc-only" + MaxGroupPVCKey = "max-group-pvcs" + defaultMaxGroupPVC = 100 // based on ceph's support/testing ) // NewConfig returns a new Config object with default values. @@ -54,6 +57,7 @@ func NewConfig() Config { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: defaultMaxGroupPVC, } } @@ -99,6 +103,17 @@ func (cfg *Config) readConfig(dataMap map[string]string) error { } cfg.SchedulePrecedence = val + case MaxGroupPVCKey: + maxGroupPVCs, err := strconv.Atoi(val) + if err != nil { + return fmt.Errorf("failed to parse key %q value %q as int: %w", + MaxGroupPVCKey, val, err) + } + if maxGroupPVCs <= 0 || maxGroupPVCs > 100 { + return fmt.Errorf("invalid value %q for key %q", val, MaxGroupPVCKey) + } + cfg.MaxGroupPVC = maxGroupPVCs + default: return fmt.Errorf("unknown config key %q", key) } diff --git a/internal/util/config_test.go b/internal/util/config_test.go index a7dcacfb3..7a047ec54 100644 --- a/internal/util/config_test.go +++ b/internal/util/config_test.go @@ -38,6 +38,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: false, }, @@ -49,6 +50,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: false, }, @@ -62,6 +64,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: time.Minute * 10, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: false, }, @@ -75,6 +78,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: true, }, @@ -88,6 +92,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: 1, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: false, }, @@ -101,6 +106,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: true, }, @@ -115,6 +121,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: time.Minute * 10, MaxConcurrentReconciles: 5, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: false, }, @@ -128,6 +135,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: true, }, @@ -141,6 +149,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: ScheduleSCOnly, + MaxGroupPVC: 100, }, wantErr: false, }, @@ -154,6 +163,7 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: true, }, @@ -167,6 +177,49 @@ func TestConfigReadConfigFile(t *testing.T) { ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, MaxConcurrentReconciles: defaultMaxConcurrentReconciles, SchedulePrecedence: "", + MaxGroupPVC: 100, + }, + wantErr: true, + }, + { + name: "config file has empty max-group-pvcs", + dataMap: map[string]string{ + "max-group-pvcs": "", + }, + newConfig: Config{ + Namespace: defaultNamespace, + ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, + MaxConcurrentReconciles: defaultMaxConcurrentReconciles, + SchedulePrecedence: "", + MaxGroupPVC: 100, + }, + wantErr: true, + }, + { + name: "config file modifies max-group-pvcs", + dataMap: map[string]string{ + "max-group-pvcs": "25", + }, + newConfig: Config{ + Namespace: defaultNamespace, + ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, + MaxConcurrentReconciles: defaultMaxConcurrentReconciles, + SchedulePrecedence: "", + MaxGroupPVC: 25, + }, + wantErr: false, + }, + { + name: "config file has invalid max-group-pvcs", + dataMap: map[string]string{ + "max-group-pvcs": "200", + }, + newConfig: Config{ + Namespace: defaultNamespace, + ReclaimSpaceTimeout: defaultReclaimSpaceTimeout, + MaxConcurrentReconciles: defaultMaxConcurrentReconciles, + SchedulePrecedence: "", + MaxGroupPVC: 100, }, wantErr: true, },