Match label and fields selectors in ComponentStatus List API

This commit is contained in:
Jan Janik 2019-05-28 23:37:10 +12:00
parent 90f41951cd
commit 80cb726110
4 changed files with 89 additions and 3 deletions

2
Godeps/LICENSES generated
View File

@ -2523,7 +2523,6 @@ THE SOFTWARE.
of your accepting any such warranty or additional liability. of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work. APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following To apply the Apache License to your work, attach the following
@ -23461,4 +23460,3 @@ SOFTWARE.
= vendor/vbom.ml/util/LICENSE 9f7e1d7e8f527330ebb5f4c32e0f3e40 = vendor/vbom.ml/util/LICENSE 9f7e1d7e8f527330ebb5f4c32e0f3e40
================================================================================ ================================================================================

View File

@ -20,9 +20,13 @@ go_library(
"//pkg/probe/http:go_default_library", "//pkg/probe/http:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage:go_default_library",
], ],
) )
@ -36,7 +40,10 @@ go_test(
deps = [ deps = [
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/probe:go_default_library", "//pkg/probe:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
], ],

View File

@ -21,6 +21,11 @@ import (
"fmt" "fmt"
"sync" "sync"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -70,13 +75,47 @@ func (rs *REST) List(ctx context.Context, options *metainternalversion.ListOptio
wait.Wait() wait.Wait()
close(statuses) close(statuses)
pred := componentStatusPredicate(options)
reply := []api.ComponentStatus{} reply := []api.ComponentStatus{}
for status := range statuses { for status := range statuses {
reply = append(reply, status) // ComponentStatus resources currently (v1.14) do not support labeling, however the filtering is executed
// nonetheless in case the request contains Label or Field selectors (which will effectively filter out
// all of the results and return an empty response).
if matched := matchesPredicate(status, &pred); matched {
reply = append(reply, status)
}
} }
return &api.ComponentStatusList{Items: reply}, nil return &api.ComponentStatusList{Items: reply}, nil
} }
func componentStatusPredicate(options *metainternalversion.ListOptions) storage.SelectionPredicate {
pred := storage.SelectionPredicate{
Label: labels.Everything(),
Field: fields.Everything(),
GetAttrs: nil,
IndexFields: []string{},
}
if options != nil {
if options.LabelSelector != nil {
pred.Label = options.LabelSelector
}
if options.FieldSelector != nil {
pred.Field = options.FieldSelector
}
}
return pred
}
func matchesPredicate(status api.ComponentStatus, pred *storage.SelectionPredicate) bool {
// currently no fields except the generic meta fields are supported for predicate matching
fieldsSet := generic.AddObjectMetaFieldsSet(make(fields.Set, 2), &status.ObjectMeta, true)
return pred.MatchesObjectAttributes(
status.ObjectMeta.Labels,
fieldsSet,
)
}
func (rs *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { func (rs *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
servers := rs.GetServersToValidate() servers := rs.GetServersToValidate()

View File

@ -22,6 +22,10 @@ import (
"strings" "strings"
"testing" "testing"
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"net/http" "net/http"
"net/url" "net/url"
"time" "time"
@ -88,6 +92,44 @@ func TestList_NoError(t *testing.T) {
} }
} }
func TestList_WithLabelSelectors(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Success, data: "ok"})
opts := metainternalversion.ListOptions{
LabelSelector: labels.SelectorFromSet(map[string]string{
"testLabel": "testValue",
}),
}
got, err := r.List(genericapirequest.NewContext(), &opts)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
expect := &api.ComponentStatusList{
Items: []api.ComponentStatus{},
}
if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Got unexpected object. Diff: %s", diff.ObjectDiff(e, a))
}
}
func TestList_WithFieldSelectors(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Success, data: "ok"})
opts := metainternalversion.ListOptions{
FieldSelector: fields.SelectorFromSet(map[string]string{
"testField": "testValue",
}),
}
got, err := r.List(genericapirequest.NewContext(), &opts)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
expect := &api.ComponentStatusList{
Items: []api.ComponentStatus{},
}
if e, a := expect, got; !reflect.DeepEqual(e, a) {
t.Errorf("Got unexpected object. Diff: %s", diff.ObjectDiff(e, a))
}
}
func TestList_FailedCheck(t *testing.T) { func TestList_FailedCheck(t *testing.T) {
r := NewTestREST(testResponse{result: probe.Failure, data: ""}) r := NewTestREST(testResponse{result: probe.Failure, data: ""})
got, err := r.List(genericapirequest.NewContext(), nil) got, err := r.List(genericapirequest.NewContext(), nil)