Merge pull request #27128 from markturansky/disable_provisioning

Automatic merge from submit-queue

Allow disabling of dynamic provisioning

Allow administrators to opt-out of dynamic provisioning.  Provisioning is still on by default, which is the current behavior.

Per a conversation with @jsafrane, a boolean toggle was added and plumbed through into the controller.  Deliberate disabling will simply return nil from `provisionClaim` whereas a misconfigured provisioner will continue on and generate error events for the PVC.

@kubernetes/rh-storage @saad-ali @thockin  @abhgupta
This commit is contained in:
k8s-merge-robot 2016-06-20 02:10:43 -07:00 committed by GitHub
commit d8b463dfd2
14 changed files with 127 additions and 57 deletions

View File

@ -399,6 +399,7 @@ func StartControllers(s *options.CMServer, kubeClient *client.Client, kubeconfig
cloud, cloud,
s.ClusterName, s.ClusterName,
nil, nil, nil, nil, nil, nil,
s.VolumeConfiguration.EnableDynamicProvisioning,
) )
volumeController.Run() volumeController.Run()
time.Sleep(wait.Jitter(s.ControllerStartInterval.Duration, ControllerStartJitter)) time.Sleep(wait.Jitter(s.ControllerStartInterval.Duration, ControllerStartJitter))

View File

@ -75,6 +75,7 @@ func NewCMServer() *CMServer {
TerminatedPodGCThreshold: 12500, TerminatedPodGCThreshold: 12500,
VolumeConfiguration: componentconfig.VolumeConfiguration{ VolumeConfiguration: componentconfig.VolumeConfiguration{
EnableHostPathProvisioning: false, EnableHostPathProvisioning: false,
EnableDynamicProvisioning: true,
PersistentVolumeRecyclerConfiguration: componentconfig.PersistentVolumeRecyclerConfiguration{ PersistentVolumeRecyclerConfiguration: componentconfig.PersistentVolumeRecyclerConfiguration{
MaximumRetry: 3, MaximumRetry: 3,
MinimumTimeoutNFS: 300, MinimumTimeoutNFS: 300,
@ -125,6 +126,7 @@ func (s *CMServer) AddFlags(fs *pflag.FlagSet) {
fs.Int32Var(&s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.MinimumTimeoutHostPath, "pv-recycler-minimum-timeout-hostpath", s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.MinimumTimeoutHostPath, "The minimum ActiveDeadlineSeconds to use for a HostPath Recycler pod. This is for development and testing only and will not work in a multi-node cluster.") fs.Int32Var(&s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.MinimumTimeoutHostPath, "pv-recycler-minimum-timeout-hostpath", s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.MinimumTimeoutHostPath, "The minimum ActiveDeadlineSeconds to use for a HostPath Recycler pod. This is for development and testing only and will not work in a multi-node cluster.")
fs.Int32Var(&s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.IncrementTimeoutHostPath, "pv-recycler-timeout-increment-hostpath", s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.IncrementTimeoutHostPath, "the increment of time added per Gi to ActiveDeadlineSeconds for a HostPath scrubber pod. This is for development and testing only and will not work in a multi-node cluster.") fs.Int32Var(&s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.IncrementTimeoutHostPath, "pv-recycler-timeout-increment-hostpath", s.VolumeConfiguration.PersistentVolumeRecyclerConfiguration.IncrementTimeoutHostPath, "the increment of time added per Gi to ActiveDeadlineSeconds for a HostPath scrubber pod. This is for development and testing only and will not work in a multi-node cluster.")
fs.BoolVar(&s.VolumeConfiguration.EnableHostPathProvisioning, "enable-hostpath-provisioner", s.VolumeConfiguration.EnableHostPathProvisioning, "Enable HostPath PV provisioning when running without a cloud provider. This allows testing and development of provisioning features. HostPath provisioning is not supported in any way, won't work in a multi-node cluster, and should not be used for anything other than testing or development.") fs.BoolVar(&s.VolumeConfiguration.EnableHostPathProvisioning, "enable-hostpath-provisioner", s.VolumeConfiguration.EnableHostPathProvisioning, "Enable HostPath PV provisioning when running without a cloud provider. This allows testing and development of provisioning features. HostPath provisioning is not supported in any way, won't work in a multi-node cluster, and should not be used for anything other than testing or development.")
fs.BoolVar(&s.VolumeConfiguration.EnableDynamicProvisioning, "enable-dynamic-provisioning", s.VolumeConfiguration.EnableDynamicProvisioning, "Enable dynamic provisioning for environments that support it.")
fs.StringVar(&s.VolumeConfiguration.FlexVolumePluginDir, "flex-volume-plugin-dir", s.VolumeConfiguration.FlexVolumePluginDir, "Full path of the directory in which the flex volume plugin should search for additional third party volume plugins.") fs.StringVar(&s.VolumeConfiguration.FlexVolumePluginDir, "flex-volume-plugin-dir", s.VolumeConfiguration.FlexVolumePluginDir, "Full path of the directory in which the flex volume plugin should search for additional third party volume plugins.")
fs.Int32Var(&s.TerminatedPodGCThreshold, "terminated-pod-gc-threshold", s.TerminatedPodGCThreshold, "Number of terminated pods that can exist before the terminated pod garbage collector starts deleting terminated pods. If <= 0, the terminated pod garbage collector is disabled.") fs.Int32Var(&s.TerminatedPodGCThreshold, "terminated-pod-gc-threshold", s.TerminatedPodGCThreshold, "Number of terminated pods that can exist before the terminated pod garbage collector starts deleting terminated pods. If <= 0, the terminated pod garbage collector is disabled.")
fs.DurationVar(&s.HorizontalPodAutoscalerSyncPeriod.Duration, "horizontal-pod-autoscaler-sync-period", s.HorizontalPodAutoscalerSyncPeriod.Duration, "The period for syncing the number of pods in horizontal pod autoscaler.") fs.DurationVar(&s.HorizontalPodAutoscalerSyncPeriod.Duration, "horizontal-pod-autoscaler-sync-period", s.HorizontalPodAutoscalerSyncPeriod.Duration, "The period for syncing the number of pods in horizontal pod autoscaler.")

View File

@ -287,6 +287,7 @@ func (s *CMServer) Run(_ []string) error {
nil, nil,
nil, nil,
nil, nil,
s.VolumeConfiguration.EnableDynamicProvisioning,
) )
volumeController.Run() volumeController.Run()

View File

@ -119,6 +119,7 @@ enable-controller-attach-detach
enable-custom-metrics enable-custom-metrics
enable-debugging-handlers enable-debugging-handlers
enable-garbage-collector enable-garbage-collector
enable-dynamic-provisioning
enable-hostpath-provisioner enable-hostpath-provisioner
enable-server enable-server
enable-swagger-ui enable-swagger-ui

View File

@ -361,6 +361,7 @@ func DeepCopy_componentconfig_PortRangeVar(in PortRangeVar, out *PortRangeVar, c
func DeepCopy_componentconfig_VolumeConfiguration(in VolumeConfiguration, out *VolumeConfiguration, c *conversion.Cloner) error { func DeepCopy_componentconfig_VolumeConfiguration(in VolumeConfiguration, out *VolumeConfiguration, c *conversion.Cloner) error {
out.EnableHostPathProvisioning = in.EnableHostPathProvisioning out.EnableHostPathProvisioning = in.EnableHostPathProvisioning
out.EnableDynamicProvisioning = in.EnableDynamicProvisioning
if err := DeepCopy_componentconfig_PersistentVolumeRecyclerConfiguration(in.PersistentVolumeRecyclerConfiguration, &out.PersistentVolumeRecyclerConfiguration, c); err != nil { if err := DeepCopy_componentconfig_PersistentVolumeRecyclerConfiguration(in.PersistentVolumeRecyclerConfiguration, &out.PersistentVolumeRecyclerConfiguration, c); err != nil {
return err return err
} }

View File

@ -8914,14 +8914,14 @@ func (x *VolumeConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} else { } else {
yysep2 := !z.EncBinary() yysep2 := !z.EncBinary()
yy2arr2 := z.EncBasicHandle().StructToArray yy2arr2 := z.EncBasicHandle().StructToArray
var yyq2 [3]bool var yyq2 [4]bool
_, _, _ = yysep2, yyq2, yy2arr2 _, _, _ = yysep2, yyq2, yy2arr2
const yyr2 bool = false const yyr2 bool = false
var yynn2 int var yynn2 int
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
r.EncodeArrayStart(3) r.EncodeArrayStart(4)
} else { } else {
yynn2 = 3 yynn2 = 4
for _, b := range yyq2 { for _, b := range yyq2 {
if b { if b {
yynn2++ yynn2++
@ -8951,19 +8951,38 @@ func (x *VolumeConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) z.EncSendContainerState(codecSelfer_containerArrayElem1234)
yy7 := &x.PersistentVolumeRecyclerConfiguration yym7 := z.EncBinary()
yy7.CodecEncodeSelf(e) _ = yym7
if false {
} else {
r.EncodeBool(bool(x.EnableDynamicProvisioning))
}
} else {
z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("enableDynamicProvisioning"))
z.EncSendContainerState(codecSelfer_containerMapValue1234)
yym8 := z.EncBinary()
_ = yym8
if false {
} else {
r.EncodeBool(bool(x.EnableDynamicProvisioning))
}
}
if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234)
yy10 := &x.PersistentVolumeRecyclerConfiguration
yy10.CodecEncodeSelf(e)
} else { } else {
z.EncSendContainerState(codecSelfer_containerMapKey1234) z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("persitentVolumeRecyclerConfiguration")) r.EncodeString(codecSelferC_UTF81234, string("persitentVolumeRecyclerConfiguration"))
z.EncSendContainerState(codecSelfer_containerMapValue1234) z.EncSendContainerState(codecSelfer_containerMapValue1234)
yy9 := &x.PersistentVolumeRecyclerConfiguration yy12 := &x.PersistentVolumeRecyclerConfiguration
yy9.CodecEncodeSelf(e) yy12.CodecEncodeSelf(e)
} }
if yyr2 || yy2arr2 { if yyr2 || yy2arr2 {
z.EncSendContainerState(codecSelfer_containerArrayElem1234) z.EncSendContainerState(codecSelfer_containerArrayElem1234)
yym12 := z.EncBinary() yym15 := z.EncBinary()
_ = yym12 _ = yym15
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.FlexVolumePluginDir)) r.EncodeString(codecSelferC_UTF81234, string(x.FlexVolumePluginDir))
@ -8972,8 +8991,8 @@ func (x *VolumeConfiguration) CodecEncodeSelf(e *codec1978.Encoder) {
z.EncSendContainerState(codecSelfer_containerMapKey1234) z.EncSendContainerState(codecSelfer_containerMapKey1234)
r.EncodeString(codecSelferC_UTF81234, string("flexVolumePluginDir")) r.EncodeString(codecSelferC_UTF81234, string("flexVolumePluginDir"))
z.EncSendContainerState(codecSelfer_containerMapValue1234) z.EncSendContainerState(codecSelfer_containerMapValue1234)
yym13 := z.EncBinary() yym16 := z.EncBinary()
_ = yym13 _ = yym16
if false { if false {
} else { } else {
r.EncodeString(codecSelferC_UTF81234, string(x.FlexVolumePluginDir)) r.EncodeString(codecSelferC_UTF81234, string(x.FlexVolumePluginDir))
@ -9046,12 +9065,18 @@ func (x *VolumeConfiguration) codecDecodeSelfFromMap(l int, d *codec1978.Decoder
} else { } else {
x.EnableHostPathProvisioning = bool(r.DecodeBool()) x.EnableHostPathProvisioning = bool(r.DecodeBool())
} }
case "enableDynamicProvisioning":
if r.TryDecodeAsNil() {
x.EnableDynamicProvisioning = false
} else {
x.EnableDynamicProvisioning = bool(r.DecodeBool())
}
case "persitentVolumeRecyclerConfiguration": case "persitentVolumeRecyclerConfiguration":
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.PersistentVolumeRecyclerConfiguration = PersistentVolumeRecyclerConfiguration{} x.PersistentVolumeRecyclerConfiguration = PersistentVolumeRecyclerConfiguration{}
} else { } else {
yyv5 := &x.PersistentVolumeRecyclerConfiguration yyv6 := &x.PersistentVolumeRecyclerConfiguration
yyv5.CodecDecodeSelf(d) yyv6.CodecDecodeSelf(d)
} }
case "flexVolumePluginDir": case "flexVolumePluginDir":
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
@ -9070,16 +9095,16 @@ func (x *VolumeConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decod
var h codecSelfer1234 var h codecSelfer1234
z, r := codec1978.GenHelperDecoder(d) z, r := codec1978.GenHelperDecoder(d)
_, _, _ = h, z, r _, _, _ = h, z, r
var yyj7 int var yyj8 int
var yyb7 bool var yyb8 bool
var yyhl7 bool = l >= 0 var yyhl8 bool = l >= 0
yyj7++ yyj8++
if yyhl7 { if yyhl8 {
yyb7 = yyj7 > l yyb8 = yyj8 > l
} else { } else {
yyb7 = r.CheckBreak() yyb8 = r.CheckBreak()
} }
if yyb7 { if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return return
} }
@ -9089,13 +9114,29 @@ func (x *VolumeConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decod
} else { } else {
x.EnableHostPathProvisioning = bool(r.DecodeBool()) x.EnableHostPathProvisioning = bool(r.DecodeBool())
} }
yyj7++ yyj8++
if yyhl7 { if yyhl8 {
yyb7 = yyj7 > l yyb8 = yyj8 > l
} else { } else {
yyb7 = r.CheckBreak() yyb8 = r.CheckBreak()
} }
if yyb7 { if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return
}
z.DecSendContainerState(codecSelfer_containerArrayElem1234)
if r.TryDecodeAsNil() {
x.EnableDynamicProvisioning = false
} else {
x.EnableDynamicProvisioning = bool(r.DecodeBool())
}
yyj8++
if yyhl8 {
yyb8 = yyj8 > l
} else {
yyb8 = r.CheckBreak()
}
if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return return
} }
@ -9103,16 +9144,16 @@ func (x *VolumeConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decod
if r.TryDecodeAsNil() { if r.TryDecodeAsNil() {
x.PersistentVolumeRecyclerConfiguration = PersistentVolumeRecyclerConfiguration{} x.PersistentVolumeRecyclerConfiguration = PersistentVolumeRecyclerConfiguration{}
} else { } else {
yyv9 := &x.PersistentVolumeRecyclerConfiguration yyv11 := &x.PersistentVolumeRecyclerConfiguration
yyv9.CodecDecodeSelf(d) yyv11.CodecDecodeSelf(d)
} }
yyj7++ yyj8++
if yyhl7 { if yyhl8 {
yyb7 = yyj7 > l yyb8 = yyj8 > l
} else { } else {
yyb7 = r.CheckBreak() yyb8 = r.CheckBreak()
} }
if yyb7 { if yyb8 {
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
return return
} }
@ -9123,17 +9164,17 @@ func (x *VolumeConfiguration) codecDecodeSelfFromArray(l int, d *codec1978.Decod
x.FlexVolumePluginDir = string(r.DecodeString()) x.FlexVolumePluginDir = string(r.DecodeString())
} }
for { for {
yyj7++ yyj8++
if yyhl7 { if yyhl8 {
yyb7 = yyj7 > l yyb8 = yyj8 > l
} else { } else {
yyb7 = r.CheckBreak() yyb8 = r.CheckBreak()
} }
if yyb7 { if yyb8 {
break break
} }
z.DecSendContainerState(codecSelfer_containerArrayElem1234) z.DecSendContainerState(codecSelfer_containerArrayElem1234)
z.DecStructFieldNotFound(yyj7-1, "") z.DecStructFieldNotFound(yyj8-1, "")
} }
z.DecSendContainerState(codecSelfer_containerArrayEnd1234) z.DecSendContainerState(codecSelfer_containerArrayEnd1234)
} }

View File

@ -577,6 +577,9 @@ type VolumeConfiguration struct {
// provisioning is not supported in any way, won't work in a multi-node cluster, and // provisioning is not supported in any way, won't work in a multi-node cluster, and
// should not be used for anything other than testing or development. // should not be used for anything other than testing or development.
EnableHostPathProvisioning bool `json:"enableHostPathProvisioning"` EnableHostPathProvisioning bool `json:"enableHostPathProvisioning"`
// enableDynamicProvisioning enables the provisioning of volumes when running within an environment
// that supports dynamic provisioning. Defaults to true.
EnableDynamicProvisioning bool `json:"enableDynamicProvisioning"`
// persistentVolumeRecyclerConfiguration holds configuration for persistent volume plugins. // persistentVolumeRecyclerConfiguration holds configuration for persistent volume plugins.
PersistentVolumeRecyclerConfiguration PersistentVolumeRecyclerConfiguration `json:"persitentVolumeRecyclerConfiguration"` PersistentVolumeRecyclerConfiguration PersistentVolumeRecyclerConfiguration `json:"persitentVolumeRecyclerConfiguration"`
// volumePluginDir is the full path of the directory in which the flex // volumePluginDir is the full path of the directory in which the flex

View File

@ -106,18 +106,19 @@ const createProvisionedPVInterval = 10 * time.Second
// framework.Controllers that watch PersistentVolume and PersistentVolumeClaim // framework.Controllers that watch PersistentVolume and PersistentVolumeClaim
// changes. // changes.
type PersistentVolumeController struct { type PersistentVolumeController struct {
volumeController *framework.Controller volumeController *framework.Controller
volumeControllerStopCh chan struct{} volumeControllerStopCh chan struct{}
volumeSource cache.ListerWatcher volumeSource cache.ListerWatcher
claimController *framework.Controller claimController *framework.Controller
claimControllerStopCh chan struct{} claimControllerStopCh chan struct{}
claimSource cache.ListerWatcher claimSource cache.ListerWatcher
kubeClient clientset.Interface kubeClient clientset.Interface
eventRecorder record.EventRecorder eventRecorder record.EventRecorder
cloud cloudprovider.Interface cloud cloudprovider.Interface
recyclePluginMgr vol.VolumePluginMgr recyclePluginMgr vol.VolumePluginMgr
provisioner vol.ProvisionableVolumePlugin provisioner vol.ProvisionableVolumePlugin
clusterName string enableDynamicProvisioning bool
clusterName string
// Cache of the last known version of volumes and claims. This cache is // Cache of the last known version of volumes and claims. This cache is
// thread safe as long as the volumes/claims there are not modified, they // thread safe as long as the volumes/claims there are not modified, they
@ -1062,8 +1063,11 @@ func (ctrl *PersistentVolumeController) doDeleteVolume(volume *api.PersistentVol
return nil return nil
} }
// provisionClaim starts new asynchronous operation to provision a claim. // provisionClaim starts new asynchronous operation to provision a claim if provisioning is enabled.
func (ctrl *PersistentVolumeController) provisionClaim(claim *api.PersistentVolumeClaim) error { func (ctrl *PersistentVolumeController) provisionClaim(claim *api.PersistentVolumeClaim) error {
if !ctrl.enableDynamicProvisioning {
return nil
}
glog.V(4).Infof("provisionClaim[%s]: started", claimToClaimKey(claim)) glog.V(4).Infof("provisionClaim[%s]: started", claimToClaimKey(claim))
opName := fmt.Sprintf("provision-%s[%s]", claimToClaimKey(claim), string(claim.UID)) opName := fmt.Sprintf("provision-%s[%s]", claimToClaimKey(claim), string(claim.UID))
ctrl.scheduleOperation(opName, func() error { ctrl.scheduleOperation(opName, func() error {

View File

@ -52,6 +52,7 @@ func NewPersistentVolumeController(
clusterName string, clusterName string,
volumeSource, claimSource cache.ListerWatcher, volumeSource, claimSource cache.ListerWatcher,
eventRecorder record.EventRecorder, eventRecorder record.EventRecorder,
enableDynamicProvisioning bool,
) *PersistentVolumeController { ) *PersistentVolumeController {
if eventRecorder == nil { if eventRecorder == nil {
@ -68,6 +69,7 @@ func NewPersistentVolumeController(
runningOperations: goroutinemap.NewGoRoutineMap(), runningOperations: goroutinemap.NewGoRoutineMap(),
cloud: cloud, cloud: cloud,
provisioner: provisioner, provisioner: provisioner,
enableDynamicProvisioning: enableDynamicProvisioning,
clusterName: clusterName, clusterName: clusterName,
createProvisionedPVRetryCount: createProvisionedPVRetryCount, createProvisionedPVRetryCount: createProvisionedPVRetryCount,
createProvisionedPVInterval: createProvisionedPVInterval, createProvisionedPVInterval: createProvisionedPVInterval,

View File

@ -164,7 +164,7 @@ func TestControllerSync(t *testing.T) {
client := &fake.Clientset{} client := &fake.Clientset{}
volumeSource := framework.NewFakeControllerSource() volumeSource := framework.NewFakeControllerSource()
claimSource := framework.NewFakeControllerSource() claimSource := framework.NewFakeControllerSource()
ctrl := newTestController(client, volumeSource, claimSource) ctrl := newTestController(client, volumeSource, claimSource, true)
reactor := newVolumeReactor(client, ctrl, volumeSource, claimSource, test.errors) reactor := newVolumeReactor(client, ctrl, volumeSource, claimSource, test.errors)
for _, claim := range test.initialClaims { for _, claim := range test.initialClaims {
claimSource.Add(claim) claimSource.Add(claim)

View File

@ -555,7 +555,7 @@ func newVolumeReactor(client *fake.Clientset, ctrl *PersistentVolumeController,
return reactor return reactor
} }
func newTestController(kubeClient clientset.Interface, volumeSource, claimSource cache.ListerWatcher) *PersistentVolumeController { func newTestController(kubeClient clientset.Interface, volumeSource, claimSource cache.ListerWatcher, enableDynamicProvisioning bool) *PersistentVolumeController {
if volumeSource == nil { if volumeSource == nil {
volumeSource = framework.NewFakeControllerSource() volumeSource = framework.NewFakeControllerSource()
} }
@ -572,6 +572,7 @@ func newTestController(kubeClient clientset.Interface, volumeSource, claimSource
volumeSource, volumeSource,
claimSource, claimSource,
record.NewFakeRecorder(1000), // event recorder record.NewFakeRecorder(1000), // event recorder
enableDynamicProvisioning,
) )
// Speed up the test // Speed up the test
@ -830,7 +831,7 @@ func runSyncTests(t *testing.T, tests []controllerTest) {
// Initialize the controller // Initialize the controller
client := &fake.Clientset{} client := &fake.Clientset{}
ctrl := newTestController(client, nil, nil) ctrl := newTestController(client, nil, nil, true)
reactor := newVolumeReactor(client, ctrl, nil, nil, test.errors) reactor := newVolumeReactor(client, ctrl, nil, nil, test.errors)
for _, claim := range test.initialClaims { for _, claim := range test.initialClaims {
ctrl.claims.Add(claim) ctrl.claims.Add(claim)
@ -874,7 +875,7 @@ func runMultisyncTests(t *testing.T, tests []controllerTest) {
// Initialize the controller // Initialize the controller
client := &fake.Clientset{} client := &fake.Clientset{}
ctrl := newTestController(client, nil, nil) ctrl := newTestController(client, nil, nil, true)
reactor := newVolumeReactor(client, ctrl, nil, nil, test.errors) reactor := newVolumeReactor(client, ctrl, nil, nil, test.errors)
for _, claim := range test.initialClaims { for _, claim := range test.initialClaims {
ctrl.claims.Add(claim) ctrl.claims.Add(claim)

View File

@ -35,6 +35,7 @@ type VolumeConfigFlags struct {
PersistentVolumeRecyclerMinimumTimeoutHostPath int PersistentVolumeRecyclerMinimumTimeoutHostPath int
PersistentVolumeRecyclerIncrementTimeoutHostPath int PersistentVolumeRecyclerIncrementTimeoutHostPath int
EnableHostPathProvisioning bool EnableHostPathProvisioning bool
EnableDynamicProvisioning bool
} }
type PersistentVolumeControllerOptions struct { type PersistentVolumeControllerOptions struct {
@ -53,6 +54,7 @@ func NewPersistentVolumeControllerOptions() PersistentVolumeControllerOptions {
PersistentVolumeRecyclerMinimumTimeoutHostPath: 60, PersistentVolumeRecyclerMinimumTimeoutHostPath: 60,
PersistentVolumeRecyclerIncrementTimeoutHostPath: 30, PersistentVolumeRecyclerIncrementTimeoutHostPath: 30,
EnableHostPathProvisioning: false, EnableHostPathProvisioning: false,
EnableDynamicProvisioning: true,
}, },
} }
} }
@ -84,4 +86,6 @@ func (o *PersistentVolumeControllerOptions) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&o.VolumeConfigFlags.EnableHostPathProvisioning, "enable-hostpath-provisioner", o.VolumeConfigFlags.EnableHostPathProvisioning, fs.BoolVar(&o.VolumeConfigFlags.EnableHostPathProvisioning, "enable-hostpath-provisioner", o.VolumeConfigFlags.EnableHostPathProvisioning,
"Enable HostPath PV provisioning when running without a cloud provider. This allows testing and development of provisioning features. "+ "Enable HostPath PV provisioning when running without a cloud provider. This allows testing and development of provisioning features. "+
"HostPath provisioning is not supported in any way, won't work in a multi-node cluster, and should not be used for anything other than testing or development.") "HostPath provisioning is not supported in any way, won't work in a multi-node cluster, and should not be used for anything other than testing or development.")
fs.BoolVar(&o.VolumeConfigFlags.EnableDynamicProvisioning, "enable-dynamic-provisioning", o.VolumeConfigFlags.EnableDynamicProvisioning,
"Enable dynamic provisioning for environments that support it.")
} }

View File

@ -254,3 +254,12 @@ func TestProvisionMultiSync(t *testing.T) {
runMultisyncTests(t, tests) runMultisyncTests(t, tests)
} }
// When provisioning is disabled, provisioning a claim should instantly return nil
func TestDisablingDynamicProvisioner(t *testing.T) {
ctrl := newTestController(nil, nil, nil, false)
retVal := ctrl.provisionClaim(nil)
if retVal != nil {
t.Errorf("Expected nil return but got %v", retVal)
}
}

View File

@ -678,7 +678,7 @@ func createClients(t *testing.T, s *httptest.Server) (*clientset.Clientset, *per
cloud := &fake_cloud.FakeCloud{} cloud := &fake_cloud.FakeCloud{}
syncPeriod := getSyncPeriod() syncPeriod := getSyncPeriod()
ctrl := persistentvolumecontroller.NewPersistentVolumeController(binderClient, syncPeriod, plugin, plugins, cloud, "", nil, nil, nil) ctrl := persistentvolumecontroller.NewPersistentVolumeController(binderClient, syncPeriod, plugin, plugins, cloud, "", nil, nil, nil, true)
watchPV, err := testClient.PersistentVolumes().Watch(api.ListOptions{}) watchPV, err := testClient.PersistentVolumes().Watch(api.ListOptions{})
if err != nil { if err != nil {