// GetPodStatefulSets returns a list of StatefulSets managing a pod. Returns an error only if no matching StatefulSets are found. func (s *StoreToStatefulSetLister) GetPodStatefulSets(pod *v1.Pod) (psList []apps.StatefulSet, err error) { var selector labels.Selector var ps apps.StatefulSet if len(pod.Labels) == 0 { err = fmt.Errorf("no StatefulSets found for pod %v because it has no labels", pod.Name) return } for _, m := range s.Store.List() { ps = *m.(*apps.StatefulSet) if ps.Namespace != pod.Namespace { continue } selector, err = metav1.LabelSelectorAsSelector(ps.Spec.Selector) if err != nil { err = fmt.Errorf("invalid selector: %v", err) return } // If a StatefulSet with a nil or empty selector creeps in, it should match nothing, not everything. if selector.Empty() || !selector.Matches(labels.Set(pod.Labels)) { continue } psList = append(psList, ps) } if len(psList) == 0 { err = fmt.Errorf("could not find StatefulSet for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
// GetPodPodDisruptionBudgets returns a list of PodDisruptionBudgets matching a pod. Returns an error only if no matching PodDisruptionBudgets are found. func (s *StoreToPodDisruptionBudgetLister) GetPodPodDisruptionBudgets(pod *v1.Pod) (pdbList []policy.PodDisruptionBudget, err error) { var selector labels.Selector if len(pod.Labels) == 0 { err = fmt.Errorf("no PodDisruptionBudgets found for pod %v because it has no labels", pod.Name) return } for _, m := range s.Store.List() { pdb, ok := m.(*policy.PodDisruptionBudget) if !ok { glog.Errorf("Unexpected: %v is not a PodDisruptionBudget", m) continue } if pdb.Namespace != pod.Namespace { continue } selector, err = metav1.LabelSelectorAsSelector(pdb.Spec.Selector) if err != nil { glog.Warningf("invalid selector: %v", err) // TODO(mml): add an event to the PDB continue } // If a PDB with a nil or empty selector creeps in, it should match nothing, not everything. if selector.Empty() || !selector.Matches(labels.Set(pod.Labels)) { continue } pdbList = append(pdbList, *pdb) } if len(pdbList) == 0 { err = fmt.Errorf("could not find PodDisruptionBudget for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
// GetPodDaemonSets returns a list of daemon sets managing a pod. // Returns an error if and only if no matching daemon sets are found. func (s *StoreToDaemonSetLister) GetPodDaemonSets(pod *v1.Pod) (daemonSets []extensions.DaemonSet, err error) { var selector labels.Selector var daemonSet extensions.DaemonSet if len(pod.Labels) == 0 { err = fmt.Errorf("no daemon sets found for pod %v because it has no labels", pod.Name) return } for _, m := range s.Store.List() { daemonSet = *m.(*extensions.DaemonSet) if daemonSet.Namespace != pod.Namespace { continue } selector, err = metav1.LabelSelectorAsSelector(daemonSet.Spec.Selector) if err != nil { // this should not happen if the DaemonSet passed validation return nil, err } // If a daemonSet with a nil or empty selector creeps in, it should match nothing, not everything. if selector.Empty() || !selector.Matches(labels.Set(pod.Labels)) { continue } daemonSets = append(daemonSets, daemonSet) } if len(daemonSets) == 0 { err = fmt.Errorf("could not find daemon set for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
func (cli *HeapsterMetricsClient) GetPodMetrics(namespace string, podName string, allNamespaces bool, selector labels.Selector) ([]metricsapi.PodMetrics, error) { if allNamespaces { namespace = api.NamespaceAll } path, err := podMetricsUrl(namespace, podName) if err != nil { return []metricsapi.PodMetrics{}, err } params := map[string]string{"labelSelector": selector.String()} allMetrics := make([]metricsapi.PodMetrics, 0) resultRaw, err := GetHeapsterMetrics(cli, path, params) if err != nil { return []metricsapi.PodMetrics{}, err } if len(podName) == 0 { metrics := metricsapi.PodMetricsList{} err = json.Unmarshal(resultRaw, &metrics) if err != nil { return []metricsapi.PodMetrics{}, fmt.Errorf("failed to unmarshall heapster response: %v", err) } allMetrics = append(allMetrics, metrics.Items...) } else { var singleMetric metricsapi.PodMetrics err = json.Unmarshal(resultRaw, &singleMetric) if err != nil { return []metricsapi.PodMetrics{}, fmt.Errorf("failed to unmarshall heapster response: %v", err) } allMetrics = append(allMetrics, singleMetric) } return allMetrics, nil }
func (cli *HeapsterMetricsClient) GetNodeMetrics(nodeName string, selector labels.Selector) ([]metricsapi.NodeMetrics, error) { params := map[string]string{"labelSelector": selector.String()} path, err := nodeMetricsUrl(nodeName) if err != nil { return []metricsapi.NodeMetrics{}, err } resultRaw, err := GetHeapsterMetrics(cli, path, params) if err != nil { return []metricsapi.NodeMetrics{}, err } metrics := make([]metricsapi.NodeMetrics, 0) if len(nodeName) == 0 { metricsList := metricsapi.NodeMetricsList{} err = json.Unmarshal(resultRaw, &metricsList) if err != nil { return []metricsapi.NodeMetrics{}, fmt.Errorf("failed to unmarshall heapster response: %v", err) } metrics = append(metrics, metricsList.Items...) } else { var singleMetric metricsapi.NodeMetrics err = json.Unmarshal(resultRaw, &singleMetric) if err != nil { return []metricsapi.NodeMetrics{}, fmt.Errorf("failed to unmarshall heapster response: %v", err) } metrics = append(metrics, singleMetric) } return metrics, nil }
func (p PodsToCache) List(s labels.Selector) (selected []*v1.Pod, err error) { for _, pod := range p { if s.Matches(labels.Set(pod.Labels)) { selected = append(selected, pod) } } return selected, nil }
// List returns []*v1.Pod matching a query. func (f FakePodLister) List(s labels.Selector) (selected []*v1.Pod, err error) { for _, pod := range f { if s.Matches(labels.Set(pod.Labels)) { selected = append(selected, pod) } } return selected, nil }
// PodMatchesTermsNamespaceAndSelector returns true if the given <pod> // matches the namespace and selector defined by <affinityPod>`s <term>. func PodMatchesTermsNamespaceAndSelector(pod *v1.Pod, namespaces sets.String, selector labels.Selector) bool { if len(namespaces) != 0 && !namespaces.Has(pod.Namespace) { return false } if !selector.Matches(labels.Set(pod.Labels)) { return false } return true }
func ListAll(store Store, selector labels.Selector, appendFn AppendFunc) error { for _, m := range store.List() { metadata, err := meta.Accessor(m) if err != nil { return err } if selector.Matches(labels.Set(metadata.GetLabels())) { appendFn(m) } } return nil }
// LabelsSelectorParam adds the given selector as a query parameter func (r *Request) LabelsSelectorParam(s labels.Selector) *Request { if r.err != nil { return r } if s == nil { return r } if s.Empty() { return r } return r.setParam(metav1.LabelSelectorQueryParam(r.content.GroupVersion.String()), s.String()) }
func (cache *schedulerCache) List(selector labels.Selector) ([]*v1.Pod, error) { cache.mu.Lock() defer cache.mu.Unlock() var pods []*v1.Pod for _, info := range cache.nodes { for _, pod := range info.pods { if selector.Matches(labels.Set(pod.Labels)) { pods = append(pods, pod) } } } return pods, nil }
// getContainerRestarts returns the count of container restarts across all pods matching the given labelSelector, // and a list of nodenames across which these containers restarted. func getContainerRestarts(c clientset.Interface, ns string, labelSelector labels.Selector) (int, []string) { options := v1.ListOptions{LabelSelector: labelSelector.String()} pods, err := c.Core().Pods(ns).List(options) framework.ExpectNoError(err) failedContainers := 0 containerRestartNodes := sets.NewString() for _, p := range pods.Items { for _, v := range testutils.FailedContainers(&p) { failedContainers = failedContainers + v.Restarts containerRestartNodes.Insert(p.Spec.NodeName) } } return failedContainers, containerRestartNodes.List() }
// filterLabels returns a list of pods which have labels. func filterLabels(selectors map[string]string, cli clientset.Interface, ns string) (*v1.PodList, error) { var err error var selector labels.Selector var pl *v1.PodList // List pods based on selectors. This might be a tiny optimization rather then filtering // everything manually. if len(selectors) > 0 { selector = labels.SelectorFromSet(labels.Set(selectors)) options := v1.ListOptions{LabelSelector: selector.String()} pl, err = cli.Core().Pods(ns).List(options) } else { pl, err = cli.Core().Pods(ns).List(v1.ListOptions{}) } return pl, err }
func FilterBySelector(s labels.Selector) FilterFunc { return func(info *Info, err error) (bool, error) { if err != nil { return false, err } a, err := meta.Accessor(info.Object) if err != nil { return false, err } if !s.Matches(labels.Set(a.GetLabels())) { return false, nil } return true, nil } }
// GetPodServices gets the services that have the selector that match the labels on the given pod. func (f FakeServiceLister) GetPodServices(pod *v1.Pod) (services []*v1.Service, err error) { var selector labels.Selector for i := range f { service := f[i] // consider only services that are in the same namespace as the pod if service.Namespace != pod.Namespace { continue } selector = labels.Set(service.Spec.Selector).AsSelectorPreValidated() if selector.Matches(labels.Set(pod.Labels)) { services = append(services, service) } } return }
func (h *HeapsterMetricsClient) GetResourceMetric(resource v1.ResourceName, namespace string, selector labels.Selector) (PodResourceInfo, time.Time, error) { metricPath := fmt.Sprintf("/apis/metrics/v1alpha1/namespaces/%s/pods", namespace) params := map[string]string{"labelSelector": selector.String()} resultRaw, err := h.services. ProxyGet(h.heapsterScheme, h.heapsterService, h.heapsterPort, metricPath, params). DoRaw() if err != nil { return nil, time.Time{}, fmt.Errorf("failed to get heapster service: %v", err) } glog.V(4).Infof("Heapster metrics result: %s", string(resultRaw)) metrics := metricsapi.PodMetricsList{} err = json.Unmarshal(resultRaw, &metrics) if err != nil { return nil, time.Time{}, fmt.Errorf("failed to unmarshal heapster response: %v", err) } if len(metrics.Items) == 0 { return nil, time.Time{}, fmt.Errorf("no metrics returned from heapster") } res := make(PodResourceInfo, len(metrics.Items)) for _, m := range metrics.Items { podSum := int64(0) missing := len(m.Containers) == 0 for _, c := range m.Containers { resValue, found := c.Usage[v1.ResourceName(resource)] if !found { missing = true glog.V(2).Infof("missing resource metric %v for container %s in pod %s/%s", resource, c.Name, namespace, m.Name) continue } podSum += resValue.MilliValue() } if !missing { res[m.Name] = int64(podSum) } } timestamp := metrics.Items[0].Timestamp.Time return res, timestamp, nil }
// GetPodControllers gets the ReplicationControllers that have the selector that match the labels on the given pod func (f FakeControllerLister) GetPodControllers(pod *v1.Pod) (controllers []*v1.ReplicationController, err error) { var selector labels.Selector for i := range f { controller := f[i] if controller.Namespace != pod.Namespace { continue } selector = labels.Set(controller.Spec.Selector).AsSelectorPreValidated() if selector.Matches(labels.Set(pod.Labels)) { controllers = append(controllers, controller) } } if len(controllers) == 0 { err = fmt.Errorf("Could not find Replication Controller for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
func NewPodStore(c clientset.Interface, namespace string, label labels.Selector, field fields.Selector) *PodStore { lw := &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { options.LabelSelector = label.String() options.FieldSelector = field.String() obj, err := c.Core().Pods(namespace).List(options) return runtime.Object(obj), err }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { options.LabelSelector = label.String() options.FieldSelector = field.String() return c.Core().Pods(namespace).Watch(options) }, } store := cache.NewStore(cache.MetaNamespaceKeyFunc) stopCh := make(chan struct{}) reflector := cache.NewReflector(lw, &v1.Pod{}, store, 0) reflector.RunUntil(stopCh) return &PodStore{Store: store, stopCh: stopCh, Reflector: reflector} }
// Validates the given template and ensures that it is in accordance with the desired selector. func ValidatePodTemplateSpecForStatefulSet(template *api.PodTemplateSpec, selector labels.Selector, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if template == nil { allErrs = append(allErrs, field.Required(fldPath, "")) } else { if !selector.Empty() { // Verify that the StatefulSet selector matches the labels in template. labels := labels.Set(template.Labels) if !selector.Matches(labels) { allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`")) } } // TODO: Add validation for PodSpec, currently this will check volumes, which we know will // fail. We should really check that the union of the given volumes and volumeClaims match // volume mounts in the containers. // allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...) allErrs = append(allErrs, unversionedvalidation.ValidateLabels(template.Labels, fldPath.Child("labels"))...) allErrs = append(allErrs, apivalidation.ValidateAnnotations(template.Annotations, fldPath.Child("annotations"))...) allErrs = append(allErrs, apivalidation.ValidatePodSpecificAnnotations(template.Annotations, &template.Spec, fldPath.Child("annotations"))...) } return allErrs }
// GetPodReplicaSets gets the ReplicaSets that have the selector that match the labels on the given pod func (f FakeReplicaSetLister) GetPodReplicaSets(pod *v1.Pod) (rss []*extensions.ReplicaSet, err error) { var selector labels.Selector for _, rs := range f { if rs.Namespace != pod.Namespace { continue } selector, err = metav1.LabelSelectorAsSelector(rs.Spec.Selector) if err != nil { return } if selector.Matches(labels.Set(pod.Labels)) { rss = append(rss, rs) } } if len(rss) == 0 { err = fmt.Errorf("Could not find ReplicaSet for pod %s in namespace %s with labels: %v", pod.Name, pod.Namespace, pod.Labels) } return }
// Validates the given template and ensures that it is in accordance with the desired selector and replicas. func ValidatePodTemplateSpecForReplicaSet(template *api.PodTemplateSpec, selector labels.Selector, replicas int32, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if template == nil { allErrs = append(allErrs, field.Required(fldPath, "")) } else { if !selector.Empty() { // Verify that the ReplicaSet selector matches the labels in template. labels := labels.Set(template.Labels) if !selector.Matches(labels) { allErrs = append(allErrs, field.Invalid(fldPath.Child("metadata", "labels"), template.Labels, "`selector` does not match template `labels`")) } } allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpec(template, fldPath)...) if replicas > 1 { allErrs = append(allErrs, apivalidation.ValidateReadOnlyPersistentDisks(template.Spec.Volumes, fldPath.Child("spec", "volumes"))...) } // RestartPolicy has already been first-order validated as per ValidatePodTemplateSpec(). if template.Spec.RestartPolicy != api.RestartPolicyAlways { allErrs = append(allErrs, field.NotSupported(fldPath.Child("spec", "restartPolicy"), template.Spec.RestartPolicy, []string{string(api.RestartPolicyAlways)})) } } return allErrs }
// Wait up to 10 minutes for all matching pods to become Running and at least one // matching pod exists. func WaitForPodsWithLabelRunning(c clientset.Interface, ns string, label labels.Selector) error { running := false PodStore := NewPodStore(c, ns, label, fields.Everything()) defer PodStore.Stop() waitLoop: for start := time.Now(); time.Since(start) < 10*time.Minute; time.Sleep(5 * time.Second) { pods := PodStore.List() if len(pods) == 0 { continue waitLoop } for _, p := range pods { if p.Status.Phase != v1.PodRunning { continue waitLoop } } running = true break } if !running { return fmt.Errorf("Timeout while waiting for pods with labels %q to be running", label.String()) } return nil }
func ListAllByNamespace(indexer Indexer, namespace string, selector labels.Selector, appendFn AppendFunc) error { if namespace == v1.NamespaceAll { for _, m := range indexer.List() { metadata, err := meta.Accessor(m) if err != nil { return err } if selector.Matches(labels.Set(metadata.GetLabels())) { appendFn(m) } } return nil } items, err := indexer.Index(NamespaceIndex, &metav1.ObjectMeta{Namespace: namespace}) if err != nil { // Ignore error; do slow search without index. glog.Warningf("can not retrieve list of objects using index : %v", err) for _, m := range indexer.List() { metadata, err := meta.Accessor(m) if err != nil { return err } if metadata.GetNamespace() == namespace && selector.Matches(labels.Set(metadata.GetLabels())) { appendFn(m) } } return nil } for _, m := range items { metadata, err := meta.Accessor(m) if err != nil { return err } if selector.Matches(labels.Set(metadata.GetLabels())) { appendFn(m) } } return nil }
func (h *HeapsterMetricsClient) GetRawMetric(metricName string, namespace string, selector labels.Selector) (PodMetricsInfo, time.Time, error) { podList, err := h.podsGetter.Pods(namespace).List(v1.ListOptions{LabelSelector: selector.String()}) if err != nil { return nil, time.Time{}, fmt.Errorf("failed to get pod list while fetching metrics: %v", err) } if len(podList.Items) == 0 { return nil, time.Time{}, fmt.Errorf("no pods matched the provided selector") } podNames := make([]string, len(podList.Items)) for i, pod := range podList.Items { podNames[i] = pod.Name } now := time.Now() startTime := now.Add(heapsterQueryStart) metricPath := fmt.Sprintf("/api/v1/model/namespaces/%s/pod-list/%s/metrics/%s", namespace, strings.Join(podNames, ","), metricName) resultRaw, err := h.services. ProxyGet(h.heapsterScheme, h.heapsterService, h.heapsterPort, metricPath, map[string]string{"start": startTime.Format(time.RFC3339)}). DoRaw() if err != nil { return nil, time.Time{}, fmt.Errorf("failed to get heapster service: %v", err) } var metrics heapster.MetricResultList err = json.Unmarshal(resultRaw, &metrics) if err != nil { return nil, time.Time{}, fmt.Errorf("failed to unmarshal heapster response: %v", err) } glog.V(4).Infof("Heapster metrics result: %s", string(resultRaw)) if len(metrics.Items) != len(podNames) { // if we get too many metrics or two few metrics, we have no way of knowing which metric goes to which pod // (note that Heapster returns *empty* metric items when a pod does not exist or have that metric, so this // does not cover the "missing metric entry" case) return nil, time.Time{}, fmt.Errorf("requested metrics for %v pods, got metrics for %v", len(podNames), len(metrics.Items)) } var timestamp *time.Time res := make(PodMetricsInfo, len(metrics.Items)) for i, podMetrics := range metrics.Items { val, podTimestamp, hadMetrics := collapseTimeSamples(podMetrics, time.Minute) if hadMetrics { res[podNames[i]] = val if timestamp == nil || podTimestamp.Before(*timestamp) { timestamp = &podTimestamp } } } if timestamp == nil { timestamp = &time.Time{} } return res, *timestamp, nil }
// GetResourceReplicas calculates the desired replica count based on a target resource utilization percentage // of the given resource for pods matching the given selector in the given namespace, and the current replica count func (c *ReplicaCalculator) GetResourceReplicas(currentReplicas int32, targetUtilization int32, resource v1.ResourceName, namespace string, selector labels.Selector) (replicaCount int32, utilization int32, timestamp time.Time, err error) { metrics, timestamp, err := c.metricsClient.GetResourceMetric(resource, namespace, selector) if err != nil { return 0, 0, time.Time{}, fmt.Errorf("unable to get metrics for resource %s: %v", resource, err) } podList, err := c.podsGetter.Pods(namespace).List(v1.ListOptions{LabelSelector: selector.String()}) if err != nil { return 0, 0, time.Time{}, fmt.Errorf("unable to get pods while calculating replica count: %v", err) } if len(podList.Items) == 0 { return 0, 0, time.Time{}, fmt.Errorf("no pods returned by selector while calculating replica count") } requests := make(map[string]int64, len(podList.Items)) readyPodCount := 0 unreadyPods := sets.NewString() missingPods := sets.NewString() for _, pod := range podList.Items { podSum := int64(0) for _, container := range pod.Spec.Containers { if containerRequest, ok := container.Resources.Requests[resource]; ok { podSum += containerRequest.MilliValue() } else { return 0, 0, time.Time{}, fmt.Errorf("missing request for %s on container %s in pod %s/%s", resource, container.Name, namespace, pod.Name) } } requests[pod.Name] = podSum if pod.Status.Phase != v1.PodRunning || !v1.IsPodReady(&pod) { // save this pod name for later, but pretend it doesn't exist for now unreadyPods.Insert(pod.Name) delete(metrics, pod.Name) continue } if _, found := metrics[pod.Name]; !found { // save this pod name for later, but pretend it doesn't exist for now missingPods.Insert(pod.Name) continue } readyPodCount++ } if len(metrics) == 0 { return 0, 0, time.Time{}, fmt.Errorf("did not receive metrics for any ready pods") } usageRatio, utilization, err := metricsclient.GetResourceUtilizationRatio(metrics, requests, targetUtilization) if err != nil { return 0, 0, time.Time{}, err } rebalanceUnready := len(unreadyPods) > 0 && usageRatio > 1.0 if !rebalanceUnready && len(missingPods) == 0 { if math.Abs(1.0-usageRatio) <= tolerance { // return the current replicas if the change would be too small return currentReplicas, utilization, timestamp, nil } // if we don't have any unready or missing pods, we can calculate the new replica count now return int32(math.Ceil(usageRatio * float64(readyPodCount))), utilization, timestamp, nil } if len(missingPods) > 0 { if usageRatio < 1.0 { // on a scale-down, treat missing pods as using 100% of the resource request for podName := range missingPods { metrics[podName] = requests[podName] } } else if usageRatio > 1.0 { // on a scale-up, treat missing pods as using 0% of the resource request for podName := range missingPods { metrics[podName] = 0 } } } if rebalanceUnready { // on a scale-up, treat unready pods as using 0% of the resource request for podName := range unreadyPods { metrics[podName] = 0 } } // re-run the utilization calculation with our new numbers newUsageRatio, _, err := metricsclient.GetResourceUtilizationRatio(metrics, requests, targetUtilization) if err != nil { return 0, utilization, time.Time{}, err } if math.Abs(1.0-newUsageRatio) <= tolerance || (usageRatio < 1.0 && newUsageRatio > 1.0) || (usageRatio > 1.0 && newUsageRatio < 1.0) { // return the current replicas if the change would be too small, // or if the new usage ratio would cause a change in scale direction return currentReplicas, utilization, timestamp, nil } // return the result, where the number of replicas considered is // however many replicas factored into our calculation return int32(math.Ceil(newUsageRatio * float64(len(metrics)))), utilization, timestamp, nil }
// find returns the nearest PV from the ordered list or nil if a match is not found func (pvIndex *persistentVolumeOrderedIndex) findByClaim(claim *v1.PersistentVolumeClaim, matchPredicate matchPredicate) (*v1.PersistentVolume, error) { // PVs are indexed by their access modes to allow easier searching. Each // index is the string representation of a set of access modes. There is a // finite number of possible sets and PVs will only be indexed in one of // them (whichever index matches the PV's modes). // // A request for resources will always specify its desired access modes. // Any matching PV must have at least that number of access modes, but it // can have more. For example, a user asks for ReadWriteOnce but a GCEPD // is available, which is ReadWriteOnce+ReadOnlyMany. // // Searches are performed against a set of access modes, so we can attempt // not only the exact matching modes but also potential matches (the GCEPD // example above). allPossibleModes := pvIndex.allPossibleMatchingAccessModes(claim.Spec.AccessModes) var smallestVolume *v1.PersistentVolume var smallestVolumeSize int64 requestedQty := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] requestedSize := requestedQty.Value() requestedClass := storageutil.GetClaimStorageClass(claim) var selector labels.Selector if claim.Spec.Selector != nil { internalSelector, err := metav1.LabelSelectorAsSelector(claim.Spec.Selector) if err != nil { // should be unreachable code due to validation return nil, fmt.Errorf("error creating internal label selector for claim: %v: %v", claimToClaimKey(claim), err) } selector = internalSelector } for _, modes := range allPossibleModes { volumes, err := pvIndex.listByAccessModes(modes) if err != nil { return nil, err } // Go through all available volumes with two goals: // - find a volume that is either pre-bound by user or dynamically // provisioned for this claim. Because of this we need to loop through // all volumes. // - find the smallest matching one if there is no volume pre-bound to // the claim. for _, volume := range volumes { if isVolumeBoundToClaim(volume, claim) { // this claim and volume are pre-bound; return // the volume if the size request is satisfied, // otherwise continue searching for a match volumeQty := volume.Spec.Capacity[v1.ResourceStorage] volumeSize := volumeQty.Value() if volumeSize < requestedSize { continue } return volume, nil } // In Alpha dynamic provisioning, we do now want not match claims // with existing PVs, findByClaim must find only PVs that are // pre-bound to the claim (by dynamic provisioning). TODO: remove in // 1.5 if metav1.HasAnnotation(claim.ObjectMeta, storageutil.AlphaStorageClassAnnotation) { continue } // filter out: // - volumes bound to another claim // - volumes whose labels don't match the claim's selector, if specified // - volumes in Class that is not requested if volume.Spec.ClaimRef != nil { continue } else if selector != nil && !selector.Matches(labels.Set(volume.Labels)) { continue } if storageutil.GetVolumeStorageClass(volume) != requestedClass { continue } volumeQty := volume.Spec.Capacity[v1.ResourceStorage] volumeSize := volumeQty.Value() if volumeSize >= requestedSize { if smallestVolume == nil || smallestVolumeSize > volumeSize { smallestVolume = volume smallestVolumeSize = volumeSize } } } if smallestVolume != nil { // Found a matching volume return smallestVolume, nil } } return nil, nil }