mirror of
https://github.com/rancher/steve.git
synced 2025-09-08 18:59:58 +00:00
* changed ParseQuery projectornamespace handling * addressed comments from eric/chad and tom * fixing lint error * added resourceVersion to list * updated mocks * updated tests * Update tests after rebase --------- Co-authored-by: Tom Lebreux <tom.lebreux@suse.com>
This commit is contained in:
@@ -48,6 +48,7 @@ type WatchFilter struct {
|
||||
type ByOptionsLister interface {
|
||||
ListByOptions(ctx context.Context, lo *sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error)
|
||||
Watch(ctx context.Context, options WatchOptions, eventsCh chan<- watch.Event) error
|
||||
GetLatestResourceVersion() []string
|
||||
}
|
||||
|
||||
// this is set to a var so that it can be overridden by test code for mocking purposes
|
||||
|
@@ -44,6 +44,20 @@ func (m *MockByOptionsLister) EXPECT() *MockByOptionsListerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetLatestResourceVersion mocks base method.
|
||||
func (m *MockByOptionsLister) GetLatestResourceVersion() []string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetLatestResourceVersion")
|
||||
ret0, _ := ret[0].([]string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetLatestResourceVersion indicates an expected call of GetLatestResourceVersion.
|
||||
func (mr *MockByOptionsListerMockRecorder) GetLatestResourceVersion() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestResourceVersion", reflect.TypeOf((*MockByOptionsLister)(nil).GetLatestResourceVersion))
|
||||
}
|
||||
|
||||
// ListByOptions mocks base method.
|
||||
func (m *MockByOptionsLister) ListByOptions(ctx context.Context, lo *sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@@ -285,6 +285,16 @@ func NewListOptionIndexer(ctx context.Context, s Store, opts ListOptionIndexerOp
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *ListOptionIndexer) GetLatestResourceVersion() []string {
|
||||
var latestRV []string
|
||||
|
||||
l.latestRVLock.RLock()
|
||||
latestRV = []string{l.latestRV}
|
||||
l.latestRVLock.RUnlock()
|
||||
|
||||
return latestRV
|
||||
}
|
||||
|
||||
func (l *ListOptionIndexer) Watch(ctx context.Context, opts WatchOptions, eventsCh chan<- watch.Event) error {
|
||||
l.latestRVLock.RLock()
|
||||
latestRV := l.latestRV
|
||||
|
@@ -4,6 +4,7 @@ package listprocessor
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -152,8 +153,9 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (sqltypes.ListOpt
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
if projOrNSFilters == nil {
|
||||
return opts, apierror.NewAPIError(validation.NotFound, fmt.Sprintf("could not find any namespaces named [%s] or namespaces belonging to project named [%s]", projectsOrNamespaces, projectsOrNamespaces))
|
||||
if len(projOrNSFilters) == 0 {
|
||||
return opts, apierror.NewAPIError(validation.ErrorCode{Code: "No Data", Status: http.StatusNoContent},
|
||||
fmt.Sprintf("could not find any namespaces named [%s] or namespaces belonging to project named [%s]", projectsOrNamespaces, projectsOrNamespaces))
|
||||
}
|
||||
if op == sqltypes.NotEq {
|
||||
for _, filter := range projOrNSFilters {
|
||||
@@ -182,7 +184,7 @@ func splitQuery(query string) []string {
|
||||
}
|
||||
|
||||
func parseNamespaceOrProjectFilters(ctx context.Context, projOrNS string, op sqltypes.Op, namespaceInformer Cache) ([]sqltypes.Filter, error) {
|
||||
var filters []sqltypes.Filter
|
||||
filters := []sqltypes.Filter{}
|
||||
for _, pn := range strings.Split(projOrNS, ",") {
|
||||
uList, _, _, err := namespaceInformer.ListByOptions(ctx, &sqltypes.ListOptions{
|
||||
Filters: []sqltypes.OrFilter{
|
||||
|
@@ -152,25 +152,14 @@ func TestParseQuery(t *testing.T) {
|
||||
})
|
||||
tests = append(tests, testCase{
|
||||
description: "ParseQuery() with no errors returned should returned no errors. If projectsornamespaces is not empty" +
|
||||
" and nsc does not return namespaces, an error should be returned.",
|
||||
" and nsc does not return namespaces, it should return an empty filter array",
|
||||
req: &types.APIRequest{
|
||||
Request: &http.Request{
|
||||
URL: &url.URL{RawQuery: "projectsornamespaces=somethin"},
|
||||
},
|
||||
},
|
||||
expectedLO: sqltypes.ListOptions{
|
||||
Filters: []sqltypes.OrFilter{
|
||||
{
|
||||
Filters: []sqltypes.Filter{
|
||||
{
|
||||
Field: []string{"metadata", "namespace"},
|
||||
Matches: []string{"ns1"},
|
||||
Op: sqltypes.Eq,
|
||||
Partial: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Filters: []sqltypes.OrFilter{},
|
||||
Pagination: sqltypes.Pagination{
|
||||
Page: 1,
|
||||
},
|
||||
|
@@ -781,10 +781,6 @@ func (s *Store) Delete(apiOp *types.APIRequest, schema *types.APISchema, id stri
|
||||
// - a continue token, if there are more pages after the returned one
|
||||
// - an error instead of all of the above if anything went wrong
|
||||
func (s *Store) ListByPartitions(apiOp *types.APIRequest, apiSchema *types.APISchema, partitions []partition.Partition) (*unstructured.UnstructuredList, int, string, error) {
|
||||
opts, err := listprocessor.ParseQuery(apiOp, s.namespaceCache)
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
// warnings from inside the informer are discarded
|
||||
buffer := WarningBuffer{}
|
||||
client, err := s.clientGetter.TableAdminClient(apiOp, apiSchema, "", &buffer)
|
||||
@@ -803,6 +799,23 @@ func (s *Store) ListByPartitions(apiOp *types.APIRequest, apiSchema *types.APISc
|
||||
if err != nil {
|
||||
return nil, 0, "", fmt.Errorf("cachefor %v: %w", gvk, err)
|
||||
}
|
||||
|
||||
opts, err := listprocessor.ParseQuery(apiOp, s.namespaceCache)
|
||||
if err != nil {
|
||||
var apiError *apierror.APIError
|
||||
if errors.As(err, &apiError) {
|
||||
if apiError.Code.Status == http.StatusNoContent {
|
||||
list := &unstructured.UnstructuredList{}
|
||||
resourceVersion := inf.ByOptionsLister.GetLatestResourceVersion()
|
||||
if len(resourceVersion) > 0 {
|
||||
list.SetResourceVersion(resourceVersion[0])
|
||||
}
|
||||
return list, 0, "", nil
|
||||
}
|
||||
}
|
||||
return nil, 0, "", err
|
||||
}
|
||||
|
||||
if gvk.Group == "ext.cattle.io" && (gvk.Kind == "Token" || gvk.Kind == "Kubeconfig") {
|
||||
accessSet := accesscontrol.AccessSetFromAPIRequest(apiOp)
|
||||
// See https://github.com/rancher/rancher/blob/7266e5e624f0d610c76ab0af33e30f5b72e11f61/pkg/ext/stores/tokens/tokens.go#L1186C2-L1195C3
|
||||
|
@@ -260,6 +260,7 @@ func TestListByPartitions(t *testing.T) {
|
||||
cg := NewMockClientGetter(gomock.NewController(t))
|
||||
cf := NewMockCacheFactory(gomock.NewController(t))
|
||||
tb := NewMockTransformBuilder(gomock.NewController(t))
|
||||
ri := NewMockResourceInterface(gomock.NewController(t))
|
||||
|
||||
s := &Store{
|
||||
ctx: context.Background(),
|
||||
@@ -313,6 +314,9 @@ func TestListByPartitions(t *testing.T) {
|
||||
copy(listToReturn.Items, expectedItems)
|
||||
|
||||
nsi.EXPECT().ListByOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, 0, "", fmt.Errorf("error")).Times(2)
|
||||
cg.EXPECT().TableAdminClient(req, schema, "", &WarningBuffer{}).Return(ri, nil)
|
||||
tb.EXPECT().GetTransformFunc(attributes.GVK(schema), gomock.Any(), false).Return(func(obj interface{}) (interface{}, error) { return obj, nil })
|
||||
cf.EXPECT().CacheFor(context.Background(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), &tablelistconvert.Client{ResourceInterface: ri}, attributes.GVK(schema), attributes.Namespaced(schema), true)
|
||||
_, err := listprocessor.ParseQuery(req, nsi)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
|
@@ -45,6 +45,20 @@ func (m *MockByOptionsLister) EXPECT() *MockByOptionsListerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// GetLatestResourceVersion mocks base method.
|
||||
func (m *MockByOptionsLister) GetLatestResourceVersion() []string {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetLatestResourceVersion")
|
||||
ret0, _ := ret[0].([]string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetLatestResourceVersion indicates an expected call of GetLatestResourceVersion.
|
||||
func (mr *MockByOptionsListerMockRecorder) GetLatestResourceVersion() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestResourceVersion", reflect.TypeOf((*MockByOptionsLister)(nil).GetLatestResourceVersion))
|
||||
}
|
||||
|
||||
// ListByOptions mocks base method.
|
||||
func (m *MockByOptionsLister) ListByOptions(ctx context.Context, lo *sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
Reference in New Issue
Block a user