diff --git a/examples/persistent-volume-provisioning/README.md b/examples/persistent-volume-provisioning/README.md index 56f7dbb5d48..6ba72f0c9bd 100644 --- a/examples/persistent-volume-provisioning/README.md +++ b/examples/persistent-volume-provisioning/README.md @@ -45,11 +45,12 @@ metadata: provisioner: kubernetes.io/gce-pd parameters: type: pd-standard - zone: us-central1-a + zones: us-central1-a, us-central1-b ``` * `type`: `pd-standard` or `pd-ssd`. Default: `pd-ssd` -* `zone`: GCE zone. If not specified, a random zone in the same region as controller-manager will be chosen. +* `zone`: GCE zone. If neither zone nor zones is specified, volumes are generally round-robin-ed across all active zones where Kubernetes cluster has a node. Note: zone and zones parameters must not be used at the same time. +* `zones`: a comma separated list of GCE zone(s). If neither zone nor zones is specified, volumes are generally round-robin-ed across all active zones where Kubernetes cluster has a node. Note: zone and zones parameters must not be used at the same time. #### vSphere diff --git a/pkg/volume/gce_pd/gce_util.go b/pkg/volume/gce_pd/gce_util.go index 8d71fc0eb4f..0fe15ff0aa5 100644 --- a/pkg/volume/gce_pd/gce_util.go +++ b/pkg/volume/gce_pd/gce_util.go @@ -86,33 +86,55 @@ func (gceutil *GCEDiskUtil) CreateVolume(c *gcePersistentDiskProvisioner) (strin // Apply Parameters (case-insensitive). We leave validation of // the values to the cloud provider. diskType := "" - zone := "" + configuredZone := "" + configuredZones := "" + zonePresent := false + zonesPresent := false for k, v := range c.options.Parameters { switch strings.ToLower(k) { case "type": diskType = v case "zone": - zone = v + zonePresent = true + configuredZone = v + case "zones": + zonesPresent = true + configuredZones = v default: return "", 0, nil, fmt.Errorf("invalid option %q for volume plugin %s", k, c.plugin.GetPluginName()) } } + if zonePresent && zonesPresent { + return "", 0, nil, fmt.Errorf("both zone and zones StorageClass parameters must not be used at the same time") + } + // TODO: implement PVC.Selector parsing if c.options.PVC.Spec.Selector != nil { return "", 0, nil, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on GCE") } - if zone == "" { - // No zone specified, choose one randomly in the same region as the - // node is running. - zones, err := cloud.GetAllZones() + var zones sets.String + if !zonePresent && !zonesPresent { + zones, err = cloud.GetAllZones() if err != nil { glog.V(2).Infof("error getting zone information from GCE: %v", err) return "", 0, nil, err } - zone = volume.ChooseZoneForVolume(zones, c.options.PVC.Name) } + if !zonePresent && zonesPresent { + if zones, err = volume.ZonesToSet(configuredZones); err != nil { + return "", 0, nil, err + } + } + if zonePresent && !zonesPresent { + if err := volume.ValidateZone(configuredZone); err != nil { + return "", 0, nil, err + } + zones = make(sets.String) + zones.Insert(configuredZone) + } + zone := volume.ChooseZoneForVolume(zones, c.options.PVC.Name) err = cloud.CreateDisk(name, diskType, zone, int64(requestGB), *c.options.CloudTags) if err != nil {