Merge pull request #36889 from wojtek-t/reuse_fields_and_labels

Automatic merge from submit-queue

Reuse fields and labels

This should significantly reduce memory allocations in apiserver in large cluster.
Explanation:
- every kubelet is refreshing watch every 5-10 minutes (this generally is not causing relist - it just renews watch)
- that means, in 5000-node cluster, we are issuing ~10 watches per second
- since we don't have "watch heartbets", the watch is issued from previously received resourceVersion
- to make some assumption, let's assume pods are evenly spread across pods, and writes for them are evenly spread - that means, that a given kubelet is interested in 1 per 5000 pod changes
- with that assumption, each watch, has to process 2500 (on average) previous watch events
- for each of such even, we are currently computing fields.

This PR is fixing this problem.
This commit is contained in:
Kubernetes Submit Queue 2016-12-02 21:49:43 -08:00 committed by GitHub
commit cd560926bd
78 changed files with 558 additions and 362 deletions

View File

@ -40,8 +40,15 @@ func NewREST(config *storagebackend.Config, storageDecorator generic.StorageDeco
newListFunc := func() runtime.Object { return &testgroup.TestTypeList{} } newListFunc := func() runtime.Object { return &testgroup.TestTypeList{} }
// Usually you should reuse your RESTCreateStrategy. // Usually you should reuse your RESTCreateStrategy.
strategy := &NotNamespaceScoped{} strategy := &NotNamespaceScoped{}
getAttrs := func(obj runtime.Object) (labels.Set, fields.Set, error) {
testObj, ok := obj.(*testgroup.TestType)
if !ok {
return nil, nil, fmt.Errorf("not a TestType")
}
return labels.Set(testObj.Labels), nil, nil
}
storageInterface, _ := storageDecorator( storageInterface, _ := storageDecorator(
config, 100, &testgroup.TestType{}, prefix, strategy, newListFunc, storage.NoTriggerPublisher) config, 100, &testgroup.TestType{}, prefix, strategy, newListFunc, getAttrs, storage.NoTriggerPublisher)
store := &registry.Store{ store := &registry.Store{
NewFunc: func() runtime.Object { return &testgroup.TestType{} }, NewFunc: func() runtime.Object { return &testgroup.TestType{} },
// NewListFunc returns an object capable of storing results of an etcd list. // NewListFunc returns an object capable of storing results of an etcd list.

View File

@ -56,6 +56,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
cluster.Strategy, cluster.Strategy,
newListFunc, newListFunc,
cluster.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -45,17 +45,20 @@ func ClusterToSelectableFields(cluster *federation.Cluster) fields.Set {
return generic.ObjectMetaFieldsSet(&cluster.ObjectMeta, false) return generic.ObjectMetaFieldsSet(&cluster.ObjectMeta, false)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
cluster, ok := obj.(*federation.Cluster)
if !ok {
return nil, nil, fmt.Errorf("given object is not a cluster.")
}
return labels.Set(cluster.ObjectMeta.Labels), ClusterToSelectableFields(cluster), nil
}
func MatchCluster(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchCluster(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
cluster, ok := obj.(*federation.Cluster)
if !ok {
return nil, nil, fmt.Errorf("given object is not a cluster.")
}
return labels.Set(cluster.ObjectMeta.Labels), ClusterToSelectableFields(cluster), nil
},
} }
} }

View File

@ -45,6 +45,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
petset.Strategy, petset.Strategy,
newListFunc, newListFunc,
petset.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -102,19 +102,22 @@ func StatefulSetToSelectableFields(statefulSet *apps.StatefulSet) fields.Set {
return generic.ObjectMetaFieldsSet(&statefulSet.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&statefulSet.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
statefulSet, ok := obj.(*apps.StatefulSet)
if !ok {
return nil, nil, fmt.Errorf("given object is not an StatefulSet.")
}
return labels.Set(statefulSet.ObjectMeta.Labels), StatefulSetToSelectableFields(statefulSet), nil
}
// MatchStatefulSet is the filter used by the generic etcd backend to watch events // MatchStatefulSet is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields. // from etcd to clients of the apiserver only interested in specific labels/fields.
func MatchStatefulSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchStatefulSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
statefulSet, ok := obj.(*apps.StatefulSet)
if !ok {
return nil, nil, fmt.Errorf("given object is not an StatefulSet.")
}
return labels.Set(statefulSet.ObjectMeta.Labels), StatefulSetToSelectableFields(statefulSet), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
horizontalpodautoscaler.Strategy, horizontalpodautoscaler.Strategy,
newListFunc, newListFunc,
horizontalpodautoscaler.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -88,17 +88,20 @@ func AutoscalerToSelectableFields(hpa *autoscaling.HorizontalPodAutoscaler) fiel
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
hpa, ok := obj.(*autoscaling.HorizontalPodAutoscaler)
if !ok {
return nil, nil, fmt.Errorf("given object is not a horizontal pod autoscaler.")
}
return labels.Set(hpa.ObjectMeta.Labels), AutoscalerToSelectableFields(hpa), nil
}
func MatchAutoscaler(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchAutoscaler(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
hpa, ok := obj.(*autoscaling.HorizontalPodAutoscaler)
if !ok {
return nil, nil, fmt.Errorf("given object is not a horizontal pod autoscaler.")
}
return labels.Set(hpa.ObjectMeta.Labels), AutoscalerToSelectableFields(hpa), nil
},
} }
} }

View File

@ -45,6 +45,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
cronjob.Strategy, cronjob.Strategy,
newListFunc, newListFunc,
cronjob.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -102,19 +102,22 @@ func CronJobToSelectableFields(scheduledJob *batch.CronJob) fields.Set {
return generic.ObjectMetaFieldsSet(&scheduledJob.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&scheduledJob.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
scheduledJob, ok := obj.(*batch.CronJob)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a scheduled job.")
}
return labels.Set(scheduledJob.ObjectMeta.Labels), CronJobToSelectableFields(scheduledJob), nil
}
// MatchCronJob is the filter used by the generic etcd backend to route // MatchCronJob is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchCronJob(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchCronJob(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
scheduledJob, ok := obj.(*batch.CronJob)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a scheduled job.")
}
return labels.Set(scheduledJob.ObjectMeta.Labels), CronJobToSelectableFields(scheduledJob), nil
},
} }
} }

View File

@ -45,6 +45,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
job.Strategy, job.Strategy,
newListFunc, newListFunc,
job.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -164,19 +164,22 @@ func JobToSelectableFields(job *batch.Job) fields.Set {
return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet) return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
job, ok := obj.(*batch.Job)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a job.")
}
return labels.Set(job.ObjectMeta.Labels), JobToSelectableFields(job), nil
}
// MatchJob is the filter used by the generic etcd backend to route // MatchJob is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchJob(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchJob(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
job, ok := obj.(*batch.Job)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a job.")
}
return labels.Set(job.ObjectMeta.Labels), JobToSelectableFields(job), nil
},
} }
} }

View File

@ -47,6 +47,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *ApprovalREST) {
prefix, prefix,
csrregistry.Strategy, csrregistry.Strategy,
newListFunc, newListFunc,
csrregistry.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -168,18 +168,21 @@ func (csrApprovalStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Obje
return validation.ValidateCertificateSigningRequestUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest)) return validation.ValidateCertificateSigningRequestUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest))
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
sa, ok := obj.(*certificates.CertificateSigningRequest)
if !ok {
return nil, nil, fmt.Errorf("not a CertificateSigningRequest")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
sa, ok := obj.(*certificates.CertificateSigningRequest)
if !ok {
return nil, nil, fmt.Errorf("not a CertificateSigningRequest")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
},
} }
} }

View File

@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
configmap.Strategy, configmap.Strategy,
newListFunc, newListFunc,
configmap.GetAttrs,
storage.NoTriggerPublisher) storage.NoTriggerPublisher)
store := &registry.Store{ store := &registry.Store{

View File

@ -88,18 +88,20 @@ func ConfigMapToSelectableFields(cfg *api.ConfigMap) fields.Set {
return generic.ObjectMetaFieldsSet(&cfg.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&cfg.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
cfg, ok := obj.(*api.ConfigMap)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ConfigMap")
}
return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), nil
}
// MatchConfigMap returns a generic matcher for a given label and field selector. // MatchConfigMap returns a generic matcher for a given label and field selector.
func MatchConfigMap(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchConfigMap(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
cfg, ok := obj.(*api.ConfigMap)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ConfigMap")
}
return labels.Set(cfg.ObjectMeta.Labels), ConfigMapToSelectableFields(cfg), nil
},
} }
} }

View File

@ -69,6 +69,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
controller.Strategy, controller.Strategy,
newListFunc, newListFunc,
controller.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -118,20 +118,23 @@ func ControllerToSelectableFields(controller *api.ReplicationController) fields.
return generic.MergeFieldsSets(objectMetaFieldsSet, controllerSpecificFieldsSet) return generic.MergeFieldsSets(objectMetaFieldsSet, controllerSpecificFieldsSet)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
rc, ok := obj.(*api.ReplicationController)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a replication controller.")
}
return labels.Set(rc.ObjectMeta.Labels), ControllerToSelectableFields(rc), nil
}
// MatchController is the filter used by the generic etcd backend to route // MatchController is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchController(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchController(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
rc, ok := obj.(*api.ReplicationController)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a replication controller.")
}
return labels.Set(rc.ObjectMeta.Labels), ControllerToSelectableFields(rc), nil
},
} }
} }

View File

@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
endpoint.Strategy, endpoint.Strategy,
newListFunc, newListFunc,
endpoint.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -79,27 +79,21 @@ func (endpointsStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, nil, fmt.Errorf("invalid object type %#v", obj)
}
return endpoints.Labels, EndpointsToSelectableFields(endpoints), nil
}
// MatchEndpoints returns a generic matcher for a given label and field selector. // MatchEndpoints returns a generic matcher for a given label and field selector.
func MatchEndpoints(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate { func MatchEndpoints(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate {
return pkgstorage.SelectionPredicate{ return pkgstorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, nil, fmt.Errorf("invalid object type %#v", obj)
}
// Compute fields only if field selectors is non-empty
// (otherwise those won't be used).
// Those are generally also not needed if label selector does
// not match labels, but additional computation of it is expensive.
var endpointsFields fields.Set
if !field.Empty() {
endpointsFields = EndpointsToSelectableFields(endpoints)
}
return endpoints.Labels, endpointsFields, nil
},
} }
} }

View File

@ -70,17 +70,20 @@ func (eventStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
event, ok := obj.(*api.Event)
if !ok {
return nil, nil, fmt.Errorf("not an event")
}
return labels.Set(event.Labels), EventToSelectableFields(event), nil
}
func MatchEvent(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchEvent(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
event, ok := obj.(*api.Event)
if !ok {
return nil, nil, fmt.Errorf("not an event")
}
return labels.Set(event.Labels), EventToSelectableFields(event), nil
},
} }
} }

View File

@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
limitrange.Strategy, limitrange.Strategy,
newListFunc, newListFunc,
limitrange.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -85,16 +85,19 @@ func (limitrangeStrategy) Export(api.Context, runtime.Object, bool) error {
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
lr, ok := obj.(*api.LimitRange)
if !ok {
return nil, nil, fmt.Errorf("given object is not a limit range.")
}
return labels.Set(lr.ObjectMeta.Labels), LimitRangeToSelectableFields(lr), nil
}
func MatchLimitRange(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchLimitRange(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
lr, ok := obj.(*api.LimitRange)
if !ok {
return nil, nil, fmt.Errorf("given object is not a limit range.")
}
return labels.Set(lr.ObjectMeta.Labels), LimitRangeToSelectableFields(lr), nil
},
} }
} }

View File

@ -60,6 +60,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *FinalizeREST) {
prefix, prefix,
namespace.Strategy, namespace.Strategy,
newListFunc, newListFunc,
namespace.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -135,18 +135,21 @@ func (namespaceFinalizeStrategy) PrepareForUpdate(ctx api.Context, obj, old runt
newNamespace.Status = oldNamespace.Status newNamespace.Status = oldNamespace.Status
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
namespaceObj, ok := obj.(*api.Namespace)
if !ok {
return nil, nil, fmt.Errorf("not a namespace")
}
return labels.Set(namespaceObj.Labels), NamespaceToSelectableFields(namespaceObj), nil
}
// MatchNamespace returns a generic matcher for a given label and field selector. // MatchNamespace returns a generic matcher for a given label and field selector.
func MatchNamespace(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchNamespace(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
namespaceObj, ok := obj.(*api.Namespace)
if !ok {
return nil, nil, fmt.Errorf("not a namespace")
}
return labels.Set(namespaceObj.Labels), NamespaceToSelectableFields(namespaceObj), nil
},
} }
} }

View File

@ -79,6 +79,7 @@ func NewStorage(opts generic.RESTOptions, kubeletClientConfig client.KubeletClie
prefix, prefix,
node.Strategy, node.Strategy,
newListFunc, newListFunc,
node.GetAttrs,
node.NodeNameTriggerFunc) node.NodeNameTriggerFunc)
store := &registry.Store{ store := &registry.Store{

View File

@ -144,27 +144,21 @@ func NodeToSelectableFields(node *api.Node) fields.Set {
return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet) return generic.MergeFieldsSets(objectMetaFieldsSet, specificFieldsSet)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
nodeObj, ok := obj.(*api.Node)
if !ok {
return nil, nil, fmt.Errorf("not a node")
}
return labels.Set(nodeObj.ObjectMeta.Labels), NodeToSelectableFields(nodeObj), nil
}
// MatchNode returns a generic matcher for a given label and field selector. // MatchNode returns a generic matcher for a given label and field selector.
func MatchNode(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate { func MatchNode(label labels.Selector, field fields.Selector) pkgstorage.SelectionPredicate {
return pkgstorage.SelectionPredicate{ return pkgstorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
nodeObj, ok := obj.(*api.Node)
if !ok {
return nil, nil, fmt.Errorf("not a node")
}
// Compute fields only if field selectors is non-empty
// (otherwise those won't be used).
// Those are generally also not needed if label selector does
// not match labels, but additional computation of it is expensive.
var nodeFields fields.Set
if !field.Empty() {
nodeFields = NodeToSelectableFields(nodeObj)
}
return labels.Set(nodeObj.ObjectMeta.Labels), nodeFields, nil
},
IndexFields: []string{"metadata.name"}, IndexFields: []string{"metadata.name"},
} }
} }

View File

@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
persistentvolume.Strategy, persistentvolume.Strategy,
newListFunc, newListFunc,
persistentvolume.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -95,18 +95,21 @@ func (persistentvolumeStatusStrategy) ValidateUpdate(ctx api.Context, obj, old r
return validation.ValidatePersistentVolumeStatusUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume)) return validation.ValidatePersistentVolumeStatusUpdate(obj.(*api.PersistentVolume), old.(*api.PersistentVolume))
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeObj, ok := obj.(*api.PersistentVolume)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolume")
}
return labels.Set(persistentvolumeObj.Labels), PersistentVolumeToSelectableFields(persistentvolumeObj), nil
}
// MatchPersistentVolume returns a generic matcher for a given label and field selector. // MatchPersistentVolume returns a generic matcher for a given label and field selector.
func MatchPersistentVolumes(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchPersistentVolumes(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
persistentvolumeObj, ok := obj.(*api.PersistentVolume)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolume")
}
return labels.Set(persistentvolumeObj.Labels), PersistentVolumeToSelectableFields(persistentvolumeObj), nil
},
} }
} }

View File

@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
persistentvolumeclaim.Strategy, persistentvolumeclaim.Strategy,
newListFunc, newListFunc,
persistentvolumeclaim.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -95,18 +95,21 @@ func (persistentvolumeclaimStatusStrategy) ValidateUpdate(ctx api.Context, obj,
return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim)) return validation.ValidatePersistentVolumeClaimStatusUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolumeclaim")
}
return labels.Set(persistentvolumeclaimObj.Labels), PersistentVolumeClaimToSelectableFields(persistentvolumeclaimObj), nil
}
// MatchPersistentVolumeClaim returns a generic matcher for a given label and field selector. // MatchPersistentVolumeClaim returns a generic matcher for a given label and field selector.
func MatchPersistentVolumeClaim(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchPersistentVolumeClaim(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
persistentvolumeclaimObj, ok := obj.(*api.PersistentVolumeClaim)
if !ok {
return nil, nil, fmt.Errorf("not a persistentvolumeclaim")
}
return labels.Set(persistentvolumeclaimObj.Labels), PersistentVolumeClaimToSelectableFields(persistentvolumeclaimObj), nil
},
} }
} }

View File

@ -69,6 +69,7 @@ func NewStorage(opts generic.RESTOptions, k client.ConnectionInfoGetter, proxyTr
prefix, prefix,
pod.Strategy, pod.Strategy,
newListFunc, newListFunc,
pod.GetAttrs,
pod.NodeNameTriggerFunc, pod.NodeNameTriggerFunc,
) )

View File

@ -155,27 +155,21 @@ func (podStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object
return validation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod)) return validation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod))
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pod, ok := obj.(*api.Pod)
if !ok {
return nil, nil, fmt.Errorf("not a pod")
}
return labels.Set(pod.ObjectMeta.Labels), PodToSelectableFields(pod), nil
}
// MatchPod returns a generic matcher for a given label and field selector. // MatchPod returns a generic matcher for a given label and field selector.
func MatchPod(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchPod(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
pod, ok := obj.(*api.Pod)
if !ok {
return nil, nil, fmt.Errorf("not a pod")
}
// Compute fields only if field selectors is non-empty
// (otherwise those won't be used).
// Those are generally also not needed if label selector does
// not match labels, but additional computation of it is expensive.
var podFields fields.Set
if !field.Empty() {
podFields = PodToSelectableFields(pod)
}
return labels.Set(pod.ObjectMeta.Labels), podFields, nil
},
IndexFields: []string{"spec.nodeName"}, IndexFields: []string{"spec.nodeName"},
} }
} }

View File

@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
podtemplate.Strategy, podtemplate.Strategy,
newListFunc, newListFunc,
podtemplate.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -86,16 +86,19 @@ func PodTemplateToSelectableFields(podTemplate *api.PodTemplate) fields.Set {
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pt, ok := obj.(*api.PodTemplate)
if !ok {
return nil, nil, fmt.Errorf("given object is not a pod template.")
}
return labels.Set(pt.ObjectMeta.Labels), PodTemplateToSelectableFields(pt), nil
}
func MatchPodTemplate(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchPodTemplate(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
pt, ok := obj.(*api.PodTemplate)
if !ok {
return nil, nil, fmt.Errorf("given object is not a pod template.")
}
return labels.Set(pt.ObjectMeta.Labels), PodTemplateToSelectableFields(pt), nil
},
} }
} }

View File

@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
resourcequota.Strategy, resourcequota.Strategy,
newListFunc, newListFunc,
resourcequota.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -98,18 +98,21 @@ func (resourcequotaStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runt
return validation.ValidateResourceQuotaStatusUpdate(obj.(*api.ResourceQuota), old.(*api.ResourceQuota)) return validation.ValidateResourceQuotaStatusUpdate(obj.(*api.ResourceQuota), old.(*api.ResourceQuota))
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
resourcequotaObj, ok := obj.(*api.ResourceQuota)
if !ok {
return nil, nil, fmt.Errorf("not a resourcequota")
}
return labels.Set(resourcequotaObj.Labels), ResourceQuotaToSelectableFields(resourcequotaObj), nil
}
// MatchResourceQuota returns a generic matcher for a given label and field selector. // MatchResourceQuota returns a generic matcher for a given label and field selector.
func MatchResourceQuota(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchResourceQuota(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
resourcequotaObj, ok := obj.(*api.ResourceQuota)
if !ok {
return nil, nil, fmt.Errorf("not a resourcequota")
}
return labels.Set(resourcequotaObj.Labels), ResourceQuotaToSelectableFields(resourcequotaObj), nil
},
} }
} }

View File

@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
secret.Strategy, secret.Strategy,
newListFunc, newListFunc,
secret.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -94,18 +94,21 @@ func (s strategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
secret, ok := obj.(*api.Secret)
if !ok {
return nil, nil, fmt.Errorf("not a secret")
}
return labels.Set(secret.Labels), SelectableFields(secret), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
secret, ok := obj.(*api.Secret)
if !ok {
return nil, nil, fmt.Errorf("not a secret")
}
return labels.Set(secret.Labels), SelectableFields(secret), nil
},
} }
} }

View File

@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
service.Strategy, service.Strategy,
newListFunc, newListFunc,
service.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -101,17 +101,20 @@ func (svcStrategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
service, ok := obj.(*api.Service)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a service")
}
return labels.Set(service.ObjectMeta.Labels), ServiceToSelectableFields(service), nil
}
func MatchServices(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchServices(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
service, ok := obj.(*api.Service)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a service")
}
return labels.Set(service.ObjectMeta.Labels), ServiceToSelectableFields(service), nil
},
} }
} }

View File

@ -42,6 +42,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
serviceaccount.Strategy, serviceaccount.Strategy,
newListFunc, newListFunc,
serviceaccount.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -77,18 +77,21 @@ func (strategy) AllowUnconditionalUpdate() bool {
return true return true
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
sa, ok := obj.(*api.ServiceAccount)
if !ok {
return nil, nil, fmt.Errorf("not a serviceaccount")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
sa, ok := obj.(*api.ServiceAccount)
if !ok {
return nil, nil, fmt.Errorf("not a serviceaccount")
}
return labels.Set(sa.Labels), SelectableFields(sa), nil
},
} }
} }

View File

@ -45,6 +45,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
daemonset.Strategy, daemonset.Strategy,
newListFunc, newListFunc,
daemonset.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -110,20 +110,23 @@ func DaemonSetToSelectableFields(daemon *extensions.DaemonSet) fields.Set {
return generic.ObjectMetaFieldsSet(&daemon.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&daemon.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
ds, ok := obj.(*extensions.DaemonSet)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ds.")
}
return labels.Set(ds.ObjectMeta.Labels), DaemonSetToSelectableFields(ds), nil
}
// MatchSetDaemon is the filter used by the generic etcd backend to route // MatchSetDaemon is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchDaemonSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchDaemonSet(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
ds, ok := obj.(*extensions.DaemonSet)
if !ok {
return nil, nil, fmt.Errorf("given object is not a ds.")
}
return labels.Set(ds.ObjectMeta.Labels), DaemonSetToSelectableFields(ds), nil
},
} }
} }

View File

@ -71,6 +71,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST, *RollbackREST) {
prefix, prefix,
deployment.Strategy, deployment.Strategy,
newListFunc, newListFunc,
deployment.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -128,19 +128,22 @@ func DeploymentToSelectableFields(deployment *extensions.Deployment) fields.Set
return generic.ObjectMetaFieldsSet(&deployment.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&deployment.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
deployment, ok := obj.(*extensions.Deployment)
if !ok {
return nil, nil, fmt.Errorf("given object is not a deployment.")
}
return labels.Set(deployment.ObjectMeta.Labels), DeploymentToSelectableFields(deployment), nil
}
// MatchDeployment is the filter used by the generic etcd backend to route // MatchDeployment is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchDeployment(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchDeployment(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
deployment, ok := obj.(*extensions.Deployment)
if !ok {
return nil, nil, fmt.Errorf("given object is not a deployment.")
}
return labels.Set(deployment.ObjectMeta.Labels), DeploymentToSelectableFields(deployment), nil
},
} }
} }

View File

@ -45,6 +45,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
ingress.Strategy, ingress.Strategy,
newListFunc, newListFunc,
ingress.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -103,20 +103,23 @@ func IngressToSelectableFields(ingress *extensions.Ingress) fields.Set {
return generic.ObjectMetaFieldsSet(&ingress.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&ingress.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
ingress, ok := obj.(*extensions.Ingress)
if !ok {
return nil, nil, fmt.Errorf("Given object is not an Ingress.")
}
return labels.Set(ingress.ObjectMeta.Labels), IngressToSelectableFields(ingress), nil
}
// MatchIngress is the filter used by the generic etcd backend to ingress // MatchIngress is the filter used by the generic etcd backend to ingress
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchIngress(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchIngress(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
ingress, ok := obj.(*extensions.Ingress)
if !ok {
return nil, nil, fmt.Errorf("Given object is not an Ingress.")
}
return labels.Set(ingress.ObjectMeta.Labels), IngressToSelectableFields(ingress), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
networkpolicy.Strategy, networkpolicy.Strategy,
newListFunc, newListFunc,
networkpolicy.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -96,18 +96,21 @@ func NetworkPolicyToSelectableFields(networkPolicy *extensions.NetworkPolicy) fi
return generic.ObjectMetaFieldsSet(&networkPolicy.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&networkPolicy.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
networkPolicy, ok := obj.(*extensions.NetworkPolicy)
if !ok {
return nil, nil, fmt.Errorf("given object is not a NetworkPolicy.")
}
return labels.Set(networkPolicy.ObjectMeta.Labels), NetworkPolicyToSelectableFields(networkPolicy), nil
}
// MatchNetworkPolicy is the filter used by the generic etcd backend to watch events // MatchNetworkPolicy is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields. // from etcd to clients of the apiserver only interested in specific labels/fields.
func MatchNetworkPolicy(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchNetworkPolicy(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
networkPolicy, ok := obj.(*extensions.NetworkPolicy)
if !ok {
return nil, nil, fmt.Errorf("given object is not a NetworkPolicy.")
}
return labels.Set(networkPolicy.ObjectMeta.Labels), NetworkPolicyToSelectableFields(networkPolicy), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
podsecuritypolicy.Strategy, podsecuritypolicy.Strategy,
newListFunc, newListFunc,
podsecuritypolicy.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -74,18 +74,21 @@ func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.E
return validation.ValidatePodSecurityPolicyUpdate(old.(*extensions.PodSecurityPolicy), obj.(*extensions.PodSecurityPolicy)) return validation.ValidatePodSecurityPolicyUpdate(old.(*extensions.PodSecurityPolicy), obj.(*extensions.PodSecurityPolicy))
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
psp, ok := obj.(*extensions.PodSecurityPolicy)
if !ok {
return nil, nil, fmt.Errorf("given object is not a pod security policy.")
}
return labels.Set(psp.ObjectMeta.Labels), PodSecurityPolicyToSelectableFields(psp), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func MatchPodSecurityPolicy(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchPodSecurityPolicy(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
psp, ok := obj.(*extensions.PodSecurityPolicy)
if !ok {
return nil, nil, fmt.Errorf("given object is not a pod security policy.")
}
return labels.Set(psp.ObjectMeta.Labels), PodSecurityPolicyToSelectableFields(psp), nil
},
} }
} }

View File

@ -68,6 +68,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
replicaset.Strategy, replicaset.Strategy,
newListFunc, newListFunc,
replicaset.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -119,20 +119,23 @@ func ReplicaSetToSelectableFields(rs *extensions.ReplicaSet) fields.Set {
return generic.MergeFieldsSets(objectMetaFieldsSet, rsSpecificFieldsSet) return generic.MergeFieldsSets(objectMetaFieldsSet, rsSpecificFieldsSet)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
rs, ok := obj.(*extensions.ReplicaSet)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a ReplicaSet.")
}
return labels.Set(rs.ObjectMeta.Labels), ReplicaSetToSelectableFields(rs), nil
}
// MatchReplicaSet is the filter used by the generic etcd backend to route // MatchReplicaSet is the filter used by the generic etcd backend to route
// watch events from etcd to clients of the apiserver only interested in specific // watch events from etcd to clients of the apiserver only interested in specific
// labels/fields. // labels/fields.
func MatchReplicaSet(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchReplicaSet(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
rs, ok := obj.(*extensions.ReplicaSet)
if !ok {
return nil, nil, fmt.Errorf("Given object is not a ReplicaSet.")
}
return labels.Set(rs.ObjectMeta.Labels), ReplicaSetToSelectableFields(rs), nil
},
} }
} }

View File

@ -77,18 +77,21 @@ func (strategy) AllowUnconditionalUpdate() bool {
return true return true
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
tpr, ok := obj.(*extensions.ThirdPartyResource)
if !ok {
return nil, nil, fmt.Errorf("not a ThirdPartyResource")
}
return labels.Set(tpr.Labels), SelectableFields(tpr), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
tpr, ok := obj.(*extensions.ThirdPartyResource)
if !ok {
return nil, nil, fmt.Errorf("not a ThirdPartyResource")
}
return labels.Set(tpr.Labels), SelectableFields(tpr), nil
},
} }
} }

View File

@ -74,18 +74,21 @@ func (strategy) AllowUnconditionalUpdate() bool {
return true return true
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
tprd, ok := obj.(*extensions.ThirdPartyResourceData)
if !ok {
return nil, nil, fmt.Errorf("not a ThirdPartyResourceData")
}
return labels.Set(tprd.Labels), SelectableFields(tprd), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
tprd, ok := obj.(*extensions.ThirdPartyResourceData)
if !ok {
return nil, nil, fmt.Errorf("not a ThirdPartyResourceData")
}
return labels.Set(tprd.Labels), SelectableFields(tprd), nil
},
} }
} }

View File

@ -23,6 +23,7 @@ go_library(
"//pkg/api:go_default_library", "//pkg/api:go_default_library",
"//pkg/api/rest:go_default_library", "//pkg/api/rest:go_default_library",
"//pkg/fields:go_default_library", "//pkg/fields:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library", "//pkg/runtime:go_default_library",
"//pkg/storage:go_default_library", "//pkg/storage:go_default_library",
"//pkg/storage/storagebackend:go_default_library", "//pkg/storage/storagebackend:go_default_library",

View File

@ -18,6 +18,8 @@ package registry
import ( import (
"k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/registry/generic"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage"
@ -34,6 +36,7 @@ func StorageWithCacher(
resourcePrefix string, resourcePrefix string,
scopeStrategy rest.NamespaceScopedStrategy, scopeStrategy rest.NamespaceScopedStrategy,
newListFunc func() runtime.Object, newListFunc func() runtime.Object,
getAttrsFunc func(runtime.Object) (labels.Set, fields.Set, error),
triggerFunc storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) { triggerFunc storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) {
s, d := generic.NewRawStorage(storageConfig) s, d := generic.NewRawStorage(storageConfig)
@ -46,6 +49,7 @@ func StorageWithCacher(
Type: objectType, Type: objectType,
ResourcePrefix: resourcePrefix, ResourcePrefix: resourcePrefix,
NewListFunc: newListFunc, NewListFunc: newListFunc,
GetAttrsFunc: getAttrsFunc,
TriggerPublisherFunc: triggerFunc, TriggerPublisherFunc: triggerFunc,
Codec: storageConfig.Codec, Codec: storageConfig.Codec,
} }

View File

@ -103,6 +103,11 @@ func NewTestGenericStoreRegistry(t *testing.T) (factory.DestroyFunc, *Store) {
return newTestGenericStoreRegistry(t, false) return newTestGenericStoreRegistry(t, false)
} }
func getPodAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
pod := obj.(*api.Pod)
return labels.Set{"name": pod.ObjectMeta.Name}, nil, nil
}
// matchPodName returns selection predicate that matches any pod with name in the set. // matchPodName returns selection predicate that matches any pod with name in the set.
// Makes testing simpler. // Makes testing simpler.
func matchPodName(names ...string) storage.SelectionPredicate { func matchPodName(names ...string) storage.SelectionPredicate {
@ -113,12 +118,9 @@ func matchPodName(names ...string) storage.SelectionPredicate {
panic("Labels requirement must validate successfully") panic("Labels requirement must validate successfully")
} }
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: labels.Everything().Add(*l), Label: labels.Everything().Add(*l),
Field: fields.Everything(), Field: fields.Everything(),
GetAttrs: func(obj runtime.Object) (label labels.Set, field fields.Set, err error) { GetAttrs: getPodAttrs,
pod := obj.(*api.Pod)
return labels.Set{"name": pod.ObjectMeta.Name}, nil, nil
},
} }
} }
@ -1249,6 +1251,7 @@ func newTestGenericStoreRegistry(t *testing.T, hasCacheEnabled bool) (factory.De
Type: &api.Pod{}, Type: &api.Pod{},
ResourcePrefix: podPrefix, ResourcePrefix: podPrefix,
KeyFunc: func(obj runtime.Object) (string, error) { return storage.NoNamespaceKeyFunc(podPrefix, obj) }, KeyFunc: func(obj runtime.Object) (string, error) { return storage.NoNamespaceKeyFunc(podPrefix, obj) },
GetAttrsFunc: getPodAttrs,
NewListFunc: func() runtime.Object { return &api.PodList{} }, NewListFunc: func() runtime.Object { return &api.PodList{} },
Codec: sc.Codec, Codec: sc.Codec,
} }

View File

@ -19,6 +19,8 @@ package generic
import ( import (
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage"
"k8s.io/kubernetes/pkg/storage/storagebackend" "k8s.io/kubernetes/pkg/storage/storagebackend"
@ -34,6 +36,7 @@ type StorageDecorator func(
resourcePrefix string, resourcePrefix string,
scopeStrategy rest.NamespaceScopedStrategy, scopeStrategy rest.NamespaceScopedStrategy,
newListFunc func() runtime.Object, newListFunc func() runtime.Object,
getAttrsFunc func(runtime.Object) (labels.Set, fields.Set, error),
trigger storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) trigger storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc)
// Returns given 'storageInterface' without any decoration. // Returns given 'storageInterface' without any decoration.
@ -44,6 +47,7 @@ func UndecoratedStorage(
resourcePrefix string, resourcePrefix string,
scopeStrategy rest.NamespaceScopedStrategy, scopeStrategy rest.NamespaceScopedStrategy,
newListFunc func() runtime.Object, newListFunc func() runtime.Object,
getAttrsFunc func(runtime.Object) (labels.Set, fields.Set, error),
trigger storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) { trigger storage.TriggerPublisherFunc) (storage.Interface, factory.DestroyFunc) {
return NewRawStorage(config) return NewRawStorage(config)
} }

View File

@ -45,6 +45,7 @@ func NewREST(opts generic.RESTOptions) (*REST, *StatusREST) {
prefix, prefix,
poddisruptionbudget.Strategy, poddisruptionbudget.Strategy,
newListFunc, newListFunc,
poddisruptionbudget.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -102,19 +102,22 @@ func PodDisruptionBudgetToSelectableFields(podDisruptionBudget *policy.PodDisrup
return generic.ObjectMetaFieldsSet(&podDisruptionBudget.ObjectMeta, true) return generic.ObjectMetaFieldsSet(&podDisruptionBudget.ObjectMeta, true)
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
podDisruptionBudget, ok := obj.(*policy.PodDisruptionBudget)
if !ok {
return nil, nil, fmt.Errorf("given object is not a PodDisruptionBudget.")
}
return labels.Set(podDisruptionBudget.ObjectMeta.Labels), PodDisruptionBudgetToSelectableFields(podDisruptionBudget), nil
}
// MatchPodDisruptionBudget is the filter used by the generic etcd backend to watch events // MatchPodDisruptionBudget is the filter used by the generic etcd backend to watch events
// from etcd to clients of the apiserver only interested in specific labels/fields. // from etcd to clients of the apiserver only interested in specific labels/fields.
func MatchPodDisruptionBudget(label labels.Selector, field fields.Selector) storage.SelectionPredicate { func MatchPodDisruptionBudget(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
return storage.SelectionPredicate{ return storage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
podDisruptionBudget, ok := obj.(*policy.PodDisruptionBudget)
if !ok {
return nil, nil, fmt.Errorf("given object is not a PodDisruptionBudget.")
}
return labels.Set(podDisruptionBudget.ObjectMeta.Labels), PodDisruptionBudgetToSelectableFields(podDisruptionBudget), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
clusterrole.Strategy, clusterrole.Strategy,
newListFunc, newListFunc,
clusterrole.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -101,18 +101,21 @@ func (s strategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
role, ok := obj.(*rbac.ClusterRole)
if !ok {
return nil, nil, fmt.Errorf("not a ClusterRole")
}
return labels.Set(role.Labels), SelectableFields(role), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
role, ok := obj.(*rbac.ClusterRole)
if !ok {
return nil, nil, fmt.Errorf("not a ClusterRole")
}
return labels.Set(role.Labels), SelectableFields(role), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
clusterrolebinding.Strategy, clusterrolebinding.Strategy,
newListFunc, newListFunc,
clusterrolebinding.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -101,18 +101,21 @@ func (s strategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
roleBinding, ok := obj.(*rbac.ClusterRoleBinding)
if !ok {
return nil, nil, fmt.Errorf("not a ClusterRoleBinding")
}
return labels.Set(roleBinding.Labels), SelectableFields(roleBinding), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
roleBinding, ok := obj.(*rbac.ClusterRoleBinding)
if !ok {
return nil, nil, fmt.Errorf("not a ClusterRoleBinding")
}
return labels.Set(roleBinding.Labels), SelectableFields(roleBinding), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
role.Strategy, role.Strategy,
newListFunc, newListFunc,
role.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -101,18 +101,21 @@ func (s strategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
role, ok := obj.(*rbac.Role)
if !ok {
return nil, nil, fmt.Errorf("not a Role")
}
return labels.Set(role.Labels), SelectableFields(role), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
role, ok := obj.(*rbac.Role)
if !ok {
return nil, nil, fmt.Errorf("not a Role")
}
return labels.Set(role.Labels), SelectableFields(role), nil
},
} }
} }

View File

@ -44,6 +44,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
rolebinding.Strategy, rolebinding.Strategy,
newListFunc, newListFunc,
rolebinding.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -101,18 +101,21 @@ func (s strategy) Export(ctx api.Context, obj runtime.Object, exact bool) error
return nil return nil
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
roleBinding, ok := obj.(*rbac.RoleBinding)
if !ok {
return nil, nil, fmt.Errorf("not a RoleBinding")
}
return labels.Set(roleBinding.Labels), SelectableFields(roleBinding), nil
}
// Matcher returns a generic matcher for a given label and field selector. // Matcher returns a generic matcher for a given label and field selector.
func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func Matcher(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
roleBinding, ok := obj.(*rbac.RoleBinding)
if !ok {
return nil, nil, fmt.Errorf("not a RoleBinding")
}
return labels.Set(roleBinding.Labels), SelectableFields(roleBinding), nil
},
} }
} }

View File

@ -43,6 +43,7 @@ func NewREST(opts generic.RESTOptions) *REST {
prefix, prefix,
storageclass.Strategy, storageclass.Strategy,
newListFunc, newListFunc,
storageclass.GetAttrs,
storage.NoTriggerPublisher, storage.NoTriggerPublisher,
) )

View File

@ -77,19 +77,21 @@ func (storageClassStrategy) AllowUnconditionalUpdate() bool {
return true return true
} }
// GetAttrs returns labels and fields of a given object for filtering purposes.
func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
cls, ok := obj.(*storage.StorageClass)
if !ok {
return nil, nil, fmt.Errorf("given object is not of type StorageClass")
}
return labels.Set(cls.ObjectMeta.Labels), StorageClassToSelectableFields(cls), nil
}
// MatchStorageClass returns a generic matcher for a given label and field selector. // MatchStorageClass returns a generic matcher for a given label and field selector.
func MatchStorageClasses(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate { func MatchStorageClasses(label labels.Selector, field fields.Selector) apistorage.SelectionPredicate {
return apistorage.SelectionPredicate{ return apistorage.SelectionPredicate{
Label: label, Label: label,
Field: field, Field: field,
GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) { GetAttrs: GetAttrs,
cls, ok := obj.(*storage.StorageClass)
if !ok {
return nil, nil, fmt.Errorf("given object is not of type StorageClass")
}
return labels.Set(cls.ObjectMeta.Labels), StorageClassToSelectableFields(cls), nil
},
} }
} }

View File

@ -85,6 +85,7 @@ go_test(
"//pkg/api/unversioned:go_default_library", "//pkg/api/unversioned:go_default_library",
"//pkg/fields:go_default_library", "//pkg/fields:go_default_library",
"//pkg/labels:go_default_library", "//pkg/labels:go_default_library",
"//pkg/registry/core/pod:go_default_library",
"//pkg/runtime:go_default_library", "//pkg/runtime:go_default_library",
"//pkg/storage:go_default_library", "//pkg/storage:go_default_library",
"//pkg/storage/etcd:go_default_library", "//pkg/storage/etcd:go_default_library",

View File

@ -31,6 +31,8 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
utilruntime "k8s.io/kubernetes/pkg/util/runtime" utilruntime "k8s.io/kubernetes/pkg/util/runtime"
@ -60,6 +62,9 @@ type CacherConfig struct {
// KeyFunc is used to get a key in the underyling storage for a given object. // KeyFunc is used to get a key in the underyling storage for a given object.
KeyFunc func(runtime.Object) (string, error) KeyFunc func(runtime.Object) (string, error)
// GetAttrsFunc is used to get object labels and fields.
GetAttrsFunc func(runtime.Object) (labels.Set, fields.Set, error)
// TriggerPublisherFunc is used for optimizing amount of watchers that // TriggerPublisherFunc is used for optimizing amount of watchers that
// needs to process an incoming event. // needs to process an incoming event.
TriggerPublisherFunc TriggerPublisherFunc TriggerPublisherFunc TriggerPublisherFunc
@ -126,7 +131,7 @@ func (i *indexedWatchers) terminateAll(objectType reflect.Type) {
} }
} }
type filterObjectFunc func(string, runtime.Object) bool type watchFilterFunc func(string, labels.Set, fields.Set) bool
// Cacher is responsible for serving WATCH and LIST requests for a given // Cacher is responsible for serving WATCH and LIST requests for a given
// resource from its internal cache and updating its cache in the background // resource from its internal cache and updating its cache in the background
@ -183,7 +188,7 @@ type Cacher struct {
// internal cache and updating its cache in the background based on the given // internal cache and updating its cache in the background based on the given
// configuration. // configuration.
func NewCacherFromConfig(config CacherConfig) *Cacher { func NewCacherFromConfig(config CacherConfig) *Cacher {
watchCache := newWatchCache(config.CacheCapacity, config.KeyFunc) watchCache := newWatchCache(config.CacheCapacity, config.KeyFunc, config.GetAttrsFunc)
listerWatcher := newCacherListerWatcher(config.Storage, config.ResourcePrefix, config.NewListFunc) listerWatcher := newCacherListerWatcher(config.Storage, config.ResourcePrefix, config.NewListFunc)
// Give this error when it is constructed rather than when you get the // Give this error when it is constructed rather than when you get the
@ -327,7 +332,7 @@ func (c *Cacher) Watch(ctx context.Context, key string, resourceVersion string,
c.Lock() c.Lock()
defer c.Unlock() defer c.Unlock()
forget := forgetWatcher(c, c.watcherIdx, triggerValue, triggerSupported) forget := forgetWatcher(c, c.watcherIdx, triggerValue, triggerSupported)
watcher := newCacheWatcher(watchRV, chanSize, initEvents, filterFunction(key, pred), forget) watcher := newCacheWatcher(watchRV, chanSize, initEvents, watchFilterFunction(key, pred), forget)
c.watchers.addWatcher(watcher, c.watcherIdx, triggerValue, triggerSupported) c.watchers.addWatcher(watcher, c.watcherIdx, triggerValue, triggerSupported)
c.watcherIdx++ c.watcherIdx++
@ -599,7 +604,7 @@ func forgetWatcher(c *Cacher, index int, triggerValue string, triggerSupported b
} }
} }
func filterFunction(key string, p SelectionPredicate) filterObjectFunc { func filterFunction(key string, p SelectionPredicate) func(string, runtime.Object) bool {
f := SimpleFilter(p) f := SimpleFilter(p)
filterFunc := func(objKey string, obj runtime.Object) bool { filterFunc := func(objKey string, obj runtime.Object) bool {
if !hasPathPrefix(objKey, key) { if !hasPathPrefix(objKey, key) {
@ -610,6 +615,16 @@ func filterFunction(key string, p SelectionPredicate) filterObjectFunc {
return filterFunc return filterFunc
} }
func watchFilterFunction(key string, p SelectionPredicate) watchFilterFunc {
filterFunc := func(objKey string, label labels.Set, field fields.Set) bool {
if !hasPathPrefix(objKey, key) {
return false
}
return p.MatchesLabelsAndFields(label, field)
}
return filterFunc
}
// Returns resource version to which the underlying cache is synced. // Returns resource version to which the underlying cache is synced.
func (c *Cacher) LastSyncResourceVersion() (uint64, error) { func (c *Cacher) LastSyncResourceVersion() (uint64, error) {
c.ready.wait() c.ready.wait()
@ -696,12 +711,12 @@ type cacheWatcher struct {
sync.Mutex sync.Mutex
input chan watchCacheEvent input chan watchCacheEvent
result chan watch.Event result chan watch.Event
filter filterObjectFunc filter watchFilterFunc
stopped bool stopped bool
forget func(bool) forget func(bool)
} }
func newCacheWatcher(resourceVersion uint64, chanSize int, initEvents []watchCacheEvent, filter filterObjectFunc, forget func(bool)) *cacheWatcher { func newCacheWatcher(resourceVersion uint64, chanSize int, initEvents []watchCacheEvent, filter watchFilterFunc, forget func(bool)) *cacheWatcher {
watcher := &cacheWatcher{ watcher := &cacheWatcher{
input: make(chan watchCacheEvent, chanSize), input: make(chan watchCacheEvent, chanSize),
result: make(chan watch.Event, chanSize), result: make(chan watch.Event, chanSize),
@ -779,10 +794,10 @@ func (c *cacheWatcher) add(event *watchCacheEvent, timeout *time.Duration) {
// NOTE: sendWatchCacheEvent is assumed to not modify <event> !!! // NOTE: sendWatchCacheEvent is assumed to not modify <event> !!!
func (c *cacheWatcher) sendWatchCacheEvent(event *watchCacheEvent) { func (c *cacheWatcher) sendWatchCacheEvent(event *watchCacheEvent) {
curObjPasses := event.Type != watch.Deleted && c.filter(event.Key, event.Object) curObjPasses := event.Type != watch.Deleted && c.filter(event.Key, event.ObjLabels, event.ObjFields)
oldObjPasses := false oldObjPasses := false
if event.PrevObject != nil { if event.PrevObject != nil {
oldObjPasses = c.filter(event.Key, event.PrevObject) oldObjPasses = c.filter(event.Key, event.PrevObjLabels, event.PrevObjFields)
} }
if !curObjPasses && !oldObjPasses { if !curObjPasses && !oldObjPasses {
// Watcher is not interested in that object. // Watcher is not interested in that object.

View File

@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
corepod "k8s.io/kubernetes/pkg/registry/core/pod"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage"
etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd"
@ -60,6 +61,7 @@ func newTestCacher(s storage.Interface, cap int) *storage.Cacher {
Type: &api.Pod{}, Type: &api.Pod{},
ResourcePrefix: prefix, ResourcePrefix: prefix,
KeyFunc: func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) }, KeyFunc: func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) },
GetAttrsFunc: corepod.GetAttrs,
NewListFunc: func() runtime.Object { return &api.PodList{} }, NewListFunc: func() runtime.Object { return &api.PodList{} },
Codec: testapi.Default.Codec(), Codec: testapi.Default.Codec(),
} }

View File

@ -46,12 +46,25 @@ func (s *SelectionPredicate) Matches(obj runtime.Object) (bool, error) {
return false, err return false, err
} }
matched := s.Label.Matches(labels) matched := s.Label.Matches(labels)
if s.Field != nil { if matched && s.Field != nil {
matched = (matched && s.Field.Matches(fields)) matched = (matched && s.Field.Matches(fields))
} }
return matched, nil return matched, nil
} }
// MatchesLabelsAndFields returns true if the given labels and fields
// match s.Label and s.Field.
func (s *SelectionPredicate) MatchesLabelsAndFields(l labels.Set, f fields.Set) bool {
if s.Label.Empty() && s.Field.Empty() {
return true
}
matched := s.Label.Matches(l)
if matched && s.Field != nil {
matched = (matched && s.Field.Matches(f))
}
return matched
}
// MatchesSingle will return (name, true) if and only if s.Field matches on the object's // MatchesSingle will return (name, true) if and only if s.Field matches on the object's
// name. // name.
func (s *SelectionPredicate) MatchesSingle() (string, bool) { func (s *SelectionPredicate) MatchesSingle() (string, bool) {

View File

@ -26,6 +26,8 @@ import (
"k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/clock" "k8s.io/kubernetes/pkg/util/clock"
@ -45,7 +47,11 @@ const (
type watchCacheEvent struct { type watchCacheEvent struct {
Type watch.EventType Type watch.EventType
Object runtime.Object Object runtime.Object
ObjLabels labels.Set
ObjFields fields.Set
PrevObject runtime.Object PrevObject runtime.Object
PrevObjLabels labels.Set
PrevObjFields fields.Set
Key string Key string
ResourceVersion uint64 ResourceVersion uint64
} }
@ -93,6 +99,9 @@ type watchCache struct {
// keyFunc is used to get a key in the underlying storage for a given object. // keyFunc is used to get a key in the underlying storage for a given object.
keyFunc func(runtime.Object) (string, error) keyFunc func(runtime.Object) (string, error)
// getAttrsFunc is used to get labels and fields of an object.
getAttrsFunc func(runtime.Object) (labels.Set, fields.Set, error)
// cache is used a cyclic buffer - its first element (with the smallest // cache is used a cyclic buffer - its first element (with the smallest
// resourceVersion) is defined by startIndex, its last element is defined // resourceVersion) is defined by startIndex, its last element is defined
// by endIndex (if cache is full it will be startIndex + capacity). // by endIndex (if cache is full it will be startIndex + capacity).
@ -122,10 +131,14 @@ type watchCache struct {
clock clock.Clock clock clock.Clock
} }
func newWatchCache(capacity int, keyFunc func(runtime.Object) (string, error)) *watchCache { func newWatchCache(
capacity int,
keyFunc func(runtime.Object) (string, error),
getAttrsFunc func(runtime.Object) (labels.Set, fields.Set, error)) *watchCache {
wc := &watchCache{ wc := &watchCache{
capacity: capacity, capacity: capacity,
keyFunc: keyFunc, keyFunc: keyFunc,
getAttrsFunc: getAttrsFunc,
cache: make([]watchCacheElement, capacity), cache: make([]watchCacheElement, capacity),
startIndex: 0, startIndex: 0,
endIndex: 0, endIndex: 0,
@ -213,14 +226,28 @@ func (w *watchCache) processEvent(event watch.Event, resourceVersion uint64, upd
if err != nil { if err != nil {
return err return err
} }
objLabels, objFields, err := w.getAttrsFunc(event.Object)
if err != nil {
return err
}
var prevObject runtime.Object var prevObject runtime.Object
var prevObjLabels labels.Set
var prevObjFields fields.Set
if exists { if exists {
prevObject = previous.(*storeElement).Object prevObject = previous.(*storeElement).Object
prevObjLabels, prevObjFields, err = w.getAttrsFunc(prevObject)
if err != nil {
return err
}
} }
watchCacheEvent := watchCacheEvent{ watchCacheEvent := watchCacheEvent{
Type: event.Type, Type: event.Type,
Object: event.Object, Object: event.Object,
ObjLabels: objLabels,
ObjFields: objFields,
PrevObject: prevObject, PrevObject: prevObject,
PrevObjLabels: prevObjLabels,
PrevObjFields: prevObjFields,
Key: key, Key: key,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
} }
@ -394,9 +421,15 @@ func (w *watchCache) GetAllEventsSinceThreadUnsafe(resourceVersion uint64) ([]wa
if !ok { if !ok {
return nil, fmt.Errorf("not a storeElement: %v", elem) return nil, fmt.Errorf("not a storeElement: %v", elem)
} }
objLabels, objFields, err := w.getAttrsFunc(elem.Object)
if err != nil {
return nil, err
}
result[i] = watchCacheEvent{ result[i] = watchCacheEvent{
Type: watch.Added, Type: watch.Added,
Object: elem.Object, Object: elem.Object,
ObjLabels: objLabels,
ObjFields: objFields,
Key: elem.Key, Key: elem.Key,
ResourceVersion: w.resourceVersion, ResourceVersion: w.resourceVersion,
} }

View File

@ -26,6 +26,8 @@ import (
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/cache"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/clock" "k8s.io/kubernetes/pkg/util/clock"
"k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/sets"
@ -48,7 +50,10 @@ func newTestWatchCache(capacity int) *watchCache {
keyFunc := func(obj runtime.Object) (string, error) { keyFunc := func(obj runtime.Object) (string, error) {
return NamespaceKeyFunc("prefix", obj) return NamespaceKeyFunc("prefix", obj)
} }
wc := newWatchCache(capacity, keyFunc) getAttrsFunc := func(obj runtime.Object) (labels.Set, fields.Set, error) {
return nil, nil, nil
}
wc := newWatchCache(capacity, keyFunc, getAttrsFunc)
wc.clock = clock.NewFakeClock(time.Now()) wc.clock = clock.NewFakeClock(time.Now())
return wc return wc
} }