diff --git a/pkg/sqlcache/informer/informer.go b/pkg/sqlcache/informer/informer.go index 8ba193f7..7e240a23 100644 --- a/pkg/sqlcache/informer/informer.go +++ b/pkg/sqlcache/informer/informer.go @@ -10,6 +10,7 @@ import ( "github.com/rancher/steve/pkg/sqlcache/db" "github.com/rancher/steve/pkg/sqlcache/partition" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" sqlStore "github.com/rancher/steve/pkg/sqlcache/store" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -29,7 +30,7 @@ type Informer struct { } type ByOptionsLister interface { - ListByOptions(ctx context.Context, lo ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) + ListByOptions(ctx context.Context, lo sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) } // this is set to a var so that it can be overridden by test code for mocking purposes @@ -65,7 +66,7 @@ func NewInformer(ctx context.Context, client dynamic.ResourceInterface, fields [ // copy of the known state of all objects (in an Indexer). // The resync period option here is passed from Informer to Reflector to periodically (re)-push all known // objects to the DeltaFIFO. That causes the periodic (re-)firing all registered handlers. - // In this case we are not registering any handlers to this particular informer, so re-syncing is a no-op. + // sqltypes.In this case we are not registering any handlers to this particular informer, so re-syncing is a no-op. // We therefore just disable it right away. resyncPeriod := time.Duration(0) @@ -102,7 +103,7 @@ func NewInformer(ctx context.Context, client dynamic.ResourceInterface, fields [ // - the total number of resources (returned list might be a subset depending on pagination options in lo) // - 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 (i *Informer) ListByOptions(ctx context.Context, lo ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) { +func (i *Informer) ListByOptions(ctx context.Context, lo sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) { return i.ByOptionsLister.ListByOptions(ctx, lo, partitions, namespace) } diff --git a/pkg/sqlcache/informer/informer_mocks_test.go b/pkg/sqlcache/informer/informer_mocks_test.go index 9eff0612..8e91e4fa 100644 --- a/pkg/sqlcache/informer/informer_mocks_test.go +++ b/pkg/sqlcache/informer/informer_mocks_test.go @@ -14,6 +14,7 @@ import ( reflect "reflect" partition "github.com/rancher/steve/pkg/sqlcache/partition" + sqltypes "github.com/rancher/steve/pkg/sqlcache/sqltypes" gomock "go.uber.org/mock/gomock" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -42,7 +43,7 @@ func (m *MockByOptionsLister) EXPECT() *MockByOptionsListerMockRecorder { } // ListByOptions mocks base method. -func (m *MockByOptionsLister) ListByOptions(arg0 context.Context, arg1 ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { +func (m *MockByOptionsLister) ListByOptions(arg0 context.Context, arg1 sqltypes.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListByOptions", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*unstructured.UnstructuredList) diff --git a/pkg/sqlcache/informer/informer_test.go b/pkg/sqlcache/informer/informer_test.go index 4fe84c6a..bf3c8948 100644 --- a/pkg/sqlcache/informer/informer_test.go +++ b/pkg/sqlcache/informer/informer_test.go @@ -9,6 +9,7 @@ import ( "github.com/rancher/steve/pkg/sqlcache/db" "github.com/rancher/steve/pkg/sqlcache/partition" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -313,7 +314,7 @@ func TestInformerListByOptions(t *testing.T) { informer := &Informer{ ByOptionsLister: indexer, } - lo := ListOptions{} + lo := sqltypes.ListOptions{} var partitions []partition.Partition ns := "somens" expectedList := &unstructured.UnstructuredList{ @@ -336,7 +337,7 @@ func TestInformerListByOptions(t *testing.T) { informer := &Informer{ ByOptionsLister: indexer, } - lo := ListOptions{} + lo := sqltypes.ListOptions{} var partitions []partition.Partition ns := "somens" indexer.EXPECT().ListByOptions(context.Background(), lo, partitions, ns).Return(nil, 0, "", fmt.Errorf("error")) diff --git a/pkg/sqlcache/informer/listoption_indexer.go b/pkg/sqlcache/informer/listoption_indexer.go index b0c57547..682ef3df 100644 --- a/pkg/sqlcache/informer/listoption_indexer.go +++ b/pkg/sqlcache/informer/listoption_indexer.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/rancher/steve/pkg/sqlcache/db/transaction" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/tools/cache" @@ -247,7 +248,7 @@ func (l *ListOptionIndexer) deleteLabels(key string, tx transaction.Client) erro // - the total number of resources (returned list might be a subset depending on pagination options in lo) // - 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 (l *ListOptionIndexer) ListByOptions(ctx context.Context, lo ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) { +func (l *ListOptionIndexer) ListByOptions(ctx context.Context, lo sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) { queryInfo, err := l.constructQuery(lo, partitions, namespace, db.Sanitize(l.GetName())) if err != nil { return nil, 0, "", err @@ -266,7 +267,7 @@ type QueryInfo struct { offset int } -func (l *ListOptionIndexer) constructQuery(lo ListOptions, partitions []partition.Partition, namespace string, dbName string) (*QueryInfo, error) { +func (l *ListOptionIndexer) constructQuery(lo sqltypes.ListOptions, partitions []partition.Partition, namespace string, dbName string) (*QueryInfo, error) { ensureSortLabelsAreSelected(&lo) queryInfo := &QueryInfo{} queryUsesLabels := hasLabelFilter(lo.Filters) @@ -395,7 +396,7 @@ func (l *ListOptionIndexer) constructQuery(lo ListOptions, partitions []partitio orderByClauses := []string{} for i, field := range lo.Sort.Fields { if isLabelsFieldList(field) { - clause, sortParam, err := buildSortLabelsClause(field[2], joinTableIndexByLabelName, lo.Sort.Orders[i] == ASC) + clause, sortParam, err := buildSortLabelsClause(field[2], joinTableIndexByLabelName, lo.Sort.Orders[i] == sqltypes.ASC) if err != nil { return nil, err } @@ -407,7 +408,7 @@ func (l *ListOptionIndexer) constructQuery(lo ListOptions, partitions []partitio return queryInfo, err } direction := "ASC" - if lo.Sort.Orders[i] == DESC { + if lo.Sort.Orders[i] == sqltypes.DESC { direction = "DESC" } orderByClauses = append(orderByClauses, fmt.Sprintf(`f."%s" %s`, columnName, direction)) @@ -539,7 +540,7 @@ func (l *ListOptionIndexer) validateColumn(column string) error { } // buildORClause creates an SQLite compatible query that ORs conditions built from passed filters -func (l *ListOptionIndexer) buildORClauseFromFilters(orFilters OrFilter, dbName string, joinTableIndexByLabelName map[string]int) (string, []any, error) { +func (l *ListOptionIndexer) buildORClauseFromFilters(orFilters sqltypes.OrFilter, dbName string, joinTableIndexByLabelName map[string]int) (string, []any, error) { var params []any clauses := make([]string, 0, len(orFilters.Filters)) var newParams []any @@ -594,7 +595,7 @@ func buildSortLabelsClause(labelName string, joinTableIndexByLabelName map[strin // There are no thread-safety issues in doing this because the ListOptions object is // created in Store.ListByPartitions, and that ends up calling ListOptionIndexer.ConstructQuery. // No other goroutines access this object. -func ensureSortLabelsAreSelected(lo *ListOptions) { +func ensureSortLabelsAreSelected(lo *sqltypes.ListOptions) { if len(lo.Sort.Fields) == 0 { return } @@ -609,13 +610,13 @@ func ensureSortLabelsAreSelected(lo *ListOptions) { } // If we have sort directives but no filters, add an exists-filter for each label. if lo.Filters == nil || len(lo.Filters) == 0 { - lo.Filters = make([]OrFilter, 1) - lo.Filters[0].Filters = make([]Filter, len(unboundSortLabels)) + lo.Filters = make([]sqltypes.OrFilter, 1) + lo.Filters[0].Filters = make([]sqltypes.Filter, len(unboundSortLabels)) i := 0 for labelName := range unboundSortLabels { - lo.Filters[0].Filters[i] = Filter{ + lo.Filters[0].Filters[i] = sqltypes.Filter{ Field: []string{"metadata", "labels", labelName}, - Op: Exists, + Op: sqltypes.Exists, } i++ } @@ -636,9 +637,9 @@ func ensureSortLabelsAreSelected(lo *ListOptions) { for labelName, needsBinding := range copyUnboundSortLabels { if needsBinding { // `orFilters` is a copy of lo.Filters[i], so reference the original. - lo.Filters[i].Filters = append(lo.Filters[i].Filters, Filter{ + lo.Filters[i].Filters = append(lo.Filters[i].Filters, sqltypes.Filter{ Field: []string{"metadata", "labels", labelName}, - Op: Exists, + Op: sqltypes.Exists, }) } } @@ -653,7 +654,7 @@ func ensureSortLabelsAreSelected(lo *ListOptions) { // KEY in VALUES // KEY notin VALUES -func (l *ListOptionIndexer) getFieldFilter(filter Filter) (string, []any, error) { +func (l *ListOptionIndexer) getFieldFilter(filter sqltypes.Filter) (string, []any, error) { opString := "" escapeString := "" columnName := toColumnName(filter.Field) @@ -661,7 +662,7 @@ func (l *ListOptionIndexer) getFieldFilter(filter Filter) (string, []any, error) return "", nil, err } switch filter.Op { - case Eq: + case sqltypes.Eq: if filter.Partial { opString = "LIKE" escapeString = escapeBackslashDirective @@ -670,7 +671,7 @@ func (l *ListOptionIndexer) getFieldFilter(filter Filter) (string, []any, error) } clause := fmt.Sprintf(`f."%s" %s ?%s`, columnName, opString, escapeString) return clause, []any{formatMatchTarget(filter)}, nil - case NotEq: + case sqltypes.NotEq: if filter.Partial { opString = "NOT LIKE" escapeString = escapeBackslashDirective @@ -680,7 +681,7 @@ func (l *ListOptionIndexer) getFieldFilter(filter Filter) (string, []any, error) clause := fmt.Sprintf(`f."%s" %s ?%s`, columnName, opString, escapeString) return clause, []any{formatMatchTarget(filter)}, nil - case Lt, Gt: + case sqltypes.Lt, sqltypes.Gt: sym, target, err := prepareComparisonParameters(filter.Op, filter.Matches[0]) if err != nil { return "", nil, err @@ -688,18 +689,18 @@ func (l *ListOptionIndexer) getFieldFilter(filter Filter) (string, []any, error) clause := fmt.Sprintf(`f."%s" %s ?`, columnName, sym) return clause, []any{target}, nil - case Exists, NotExists: + case sqltypes.Exists, sqltypes.NotExists: return "", nil, errors.New("NULL and NOT NULL tests aren't supported for non-label queries") - case In: + case sqltypes.In: fallthrough - case NotIn: + case sqltypes.NotIn: target := "()" if len(filter.Matches) > 0 { target = fmt.Sprintf("(?%s)", strings.Repeat(", ?", len(filter.Matches)-1)) } opString = "IN" - if filter.Op == NotIn { + if filter.Op == sqltypes.NotIn { opString = "NOT IN" } clause := fmt.Sprintf(`f."%s" %s %s`, columnName, opString, target) @@ -713,13 +714,13 @@ func (l *ListOptionIndexer) getFieldFilter(filter Filter) (string, []any, error) return "", nil, fmt.Errorf("unrecognized operator: %s", opString) } -func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName string) (string, []any, error) { +func (l *ListOptionIndexer) getLabelFilter(index int, filter sqltypes.Filter, dbName string) (string, []any, error) { opString := "" escapeString := "" matchFmtToUse := strictMatchFmt labelName := filter.Field[2] switch filter.Op { - case Eq: + case sqltypes.Eq: if filter.Partial { opString = "LIKE" escapeString = escapeBackslashDirective @@ -730,7 +731,7 @@ func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName stri clause := fmt.Sprintf(`lt%d.label = ? AND lt%d.value %s ?%s`, index, index, opString, escapeString) return clause, []any{labelName, formatMatchTargetWithFormatter(filter.Matches[0], matchFmtToUse)}, nil - case NotEq: + case sqltypes.NotEq: if filter.Partial { opString = "NOT LIKE" escapeString = escapeBackslashDirective @@ -738,9 +739,9 @@ func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName stri } else { opString = "!=" } - subFilter := Filter{ + subFilter := sqltypes.Filter{ Field: filter.Field, - Op: NotExists, + Op: sqltypes.NotExists, } existenceClause, subParams, err := l.getLabelFilter(index, subFilter, dbName) if err != nil { @@ -750,7 +751,7 @@ func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName stri params := append(subParams, labelName, formatMatchTargetWithFormatter(filter.Matches[0], matchFmtToUse)) return clause, params, nil - case Lt, Gt: + case sqltypes.Lt, sqltypes.Gt: sym, target, err := prepareComparisonParameters(filter.Op, filter.Matches[0]) if err != nil { return "", nil, err @@ -758,18 +759,18 @@ func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName stri clause := fmt.Sprintf(`lt%d.label = ? AND lt%d.value %s ?`, index, index, sym) return clause, []any{labelName, target}, nil - case Exists: + case sqltypes.Exists: clause := fmt.Sprintf(`lt%d.label = ?`, index) return clause, []any{labelName}, nil - case NotExists: + case sqltypes.NotExists: clause := fmt.Sprintf(`o.key NOT IN (SELECT o1.key FROM "%s" o1 JOIN "%s_fields" f1 ON o1.key = f1.key LEFT OUTER JOIN "%s_labels" lt%di1 ON o1.key = lt%di1.key WHERE lt%di1.label = ?)`, dbName, dbName, dbName, index, index, index) return clause, []any{labelName}, nil - case In: + case sqltypes.In: target := "(?" if len(filter.Matches) > 0 { target += strings.Repeat(", ?", len(filter.Matches)-1) @@ -783,15 +784,15 @@ func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName stri } return clause, matches, nil - case NotIn: + case sqltypes.NotIn: target := "(?" if len(filter.Matches) > 0 { target += strings.Repeat(", ?", len(filter.Matches)-1) } target += ")" - subFilter := Filter{ + subFilter := sqltypes.Filter{ Field: filter.Field, - Op: NotExists, + Op: sqltypes.NotExists, } existenceClause, subParams, err := l.getLabelFilter(index, subFilter, dbName) if err != nil { @@ -807,21 +808,21 @@ func (l *ListOptionIndexer) getLabelFilter(index int, filter Filter, dbName stri return "", nil, fmt.Errorf("unrecognized operator: %s", opString) } -func prepareComparisonParameters(op Op, target string) (string, float64, error) { +func prepareComparisonParameters(op sqltypes.Op, target string) (string, float64, error) { num, err := strconv.ParseFloat(target, 32) if err != nil { return "", 0, err } switch op { - case Lt: + case sqltypes.Lt: return "<", num, nil - case Gt: + case sqltypes.Gt: return ">", num, nil } return "", 0, fmt.Errorf("unrecognized operator when expecting '<' or '>': '%s'", op) } -func formatMatchTarget(filter Filter) string { +func formatMatchTarget(filter sqltypes.Filter) string { format := strictMatchFmt if filter.Partial { format = matchFmt @@ -928,11 +929,11 @@ func extractSubFields(fields string) []string { return subfields } -func isLabelFilter(f *Filter) bool { +func isLabelFilter(f *sqltypes.Filter) bool { return len(f.Field) >= 2 && f.Field[0] == "metadata" && f.Field[1] == "labels" } -func hasLabelFilter(filters []OrFilter) bool { +func hasLabelFilter(filters []sqltypes.OrFilter) bool { for _, outerFilter := range filters { for _, filter := range outerFilter.Filters { if isLabelFilter(&filter) { diff --git a/pkg/sqlcache/informer/listoption_indexer_test.go b/pkg/sqlcache/informer/listoption_indexer_test.go index 4d7c397c..1c31f062 100644 --- a/pkg/sqlcache/informer/listoption_indexer_test.go +++ b/pkg/sqlcache/informer/listoption_indexer_test.go @@ -11,6 +11,7 @@ import ( "database/sql" "errors" "fmt" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" "reflect" "strings" "testing" @@ -255,7 +256,7 @@ func TestNewListOptionIndexer(t *testing.T) { func TestListByOptions(t *testing.T) { type testCase struct { description string - listOptions ListOptions + listOptions sqltypes.ListOptions partitions []partition.Partition ns string expectedCountStmt string @@ -276,7 +277,7 @@ func TestListByOptions(t *testing.T) { var tests []testCase tests = append(tests, testCase{ description: "ListByOptions() with no errors returned, should not return an error", - listOptions: ListOptions{}, + listOptions: sqltypes.ListOptions{}, partitions: []partition.Partition{}, ns: "", expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o @@ -291,8 +292,8 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions() with an empty filter, should not return an error", - listOptions: ListOptions{ - Filters: []OrFilter{{[]Filter{}}}, + listOptions: sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{{[]sqltypes.Filter{}}}, }, partitions: []partition.Partition{}, ns: "", @@ -307,7 +308,7 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with ChunkSize set should set limit in prepared sql.Stmt", - listOptions: ListOptions{ChunkSize: 2}, + listOptions: sqltypes.ListOptions{ChunkSize: 2}, partitions: []partition.Partition{}, ns: "", expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o @@ -329,7 +330,7 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with Resume set should set offset in prepared sql.Stmt", - listOptions: ListOptions{Resume: "4"}, + listOptions: sqltypes.ListOptions{Resume: "4"}, partitions: []partition.Partition{}, ns: "", expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o @@ -351,13 +352,13 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with 1 OrFilter set with 1 filter should select where that filter is true in prepared sql.Stmt", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"somevalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, }, @@ -380,13 +381,13 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with 1 OrFilter set with 1 filter with Op set top NotEq should select where that filter is not true in prepared sql.Stmt", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"somevalue"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: true, }, }, @@ -409,13 +410,13 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with 1 OrFilter set with 1 filter with Partial set to true should select where that partial match on that filter's value is true in prepared sql.Stmt", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"somevalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, }, @@ -438,25 +439,25 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with 1 OrFilter set with multiple filters should select where any of those filters are true in prepared sql.Stmt", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"somevalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, { Field: []string{"metadata", "somefield"}, Matches: []string{"someothervalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, { Field: []string{"metadata", "somefield"}, Matches: []string{"somethirdvalue"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: true, }, }, @@ -479,29 +480,29 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with multiple OrFilters set should select where all OrFilters contain one filter that is true in prepared sql.Stmt", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"value1"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, { Field: []string{"status", "someotherfield"}, Matches: []string{"value2"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: false, }, }, }, { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"value3"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, }, @@ -526,13 +527,13 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with labels filter should select the label in the prepared sql.Stmt", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "guard.cattle.io"}, Matches: []string{"lodgepole"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, }, @@ -558,23 +559,23 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions with two labels filters should use a self-join", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "cows"}, Matches: []string{"milk"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "horses"}, Matches: []string{"saddles"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, }, @@ -602,23 +603,23 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions with a mix of one label and one non-label query can still self-join", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "cows"}, Matches: []string{"butter"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "somefield"}, Matches: []string{"wheat"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, }, @@ -645,10 +646,10 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions with only one Sort.Field set should sort on that field only, in ascending order in prepared sql.Stmt", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}}, - Orders: []SortOrder{ASC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -668,10 +669,10 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "sort one field descending", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}}, - Orders: []SortOrder{DESC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC}, }, }, partitions: []partition.Partition{}, @@ -691,10 +692,10 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "sort one unbound label descending", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "labels", "flip"}}, - Orders: []SortOrder{DESC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC}, }, }, partitions: []partition.Partition{}, @@ -716,10 +717,10 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions sorting on two complex fields should sort on the first field in ascending order first and then sort on the second labels field in ascending order in prepared sql.Stmt", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "fields", "3"}, {"metadata", "labels", "stub.io/candy"}}, - Orders: []SortOrder{ASC, ASC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC, sqltypes.ASC}, }, }, extraIndexedFields: []string{"metadata.fields[3]", "metadata.labels[stub.io/candy]"}, @@ -740,10 +741,10 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions sorting on two fields should sort on the first field in ascending order first and then sort on the second field in ascending order in prepared sql.Stmt", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}, {"status", "someotherfield"}}, - Orders: []SortOrder{ASC, ASC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC, sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -761,10 +762,10 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions sorting on two fields should sort on the first field in descending order first and then sort on the second field in ascending order in prepared sql.Stmt", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}, {"status", "someotherfield"}}, - Orders: []SortOrder{DESC, ASC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC, sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -782,10 +783,10 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions sorting when # fields != # sort orders should return an error", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}, {"status", "someotherfield"}}, - Orders: []SortOrder{DESC, ASC, ASC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC, sqltypes.ASC, sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -803,8 +804,8 @@ func TestListByOptions(t *testing.T) { tests = append(tests, testCase{ description: "ListByOptions with Pagination.PageSize set should set limit to PageSize in prepared sql.Stmt", - listOptions: ListOptions{ - Pagination: Pagination{ + listOptions: sqltypes.ListOptions{ + Pagination: sqltypes.Pagination{ PageSize: 10, }, }, @@ -829,8 +830,8 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with Pagination.Page and no PageSize set should not add anything to prepared sql.Stmt", - listOptions: ListOptions{ - Pagination: Pagination{ + listOptions: sqltypes.ListOptions{ + Pagination: sqltypes.Pagination{ Page: 2, }, }, @@ -848,8 +849,8 @@ func TestListByOptions(t *testing.T) { }) tests = append(tests, testCase{ description: "ListByOptions with Pagination.Page and PageSize set limit to PageSize and offset to PageSize * (Page - 1) in prepared sql.Stmt", - listOptions: ListOptions{ - Pagination: Pagination{ + listOptions: sqltypes.ListOptions{ + Pagination: sqltypes.Pagination{ PageSize: 10, Page: 2, }, @@ -1028,7 +1029,7 @@ func TestListByOptions(t *testing.T) { func TestConstructQuery(t *testing.T) { type testCase struct { description string - listOptions ListOptions + listOptions sqltypes.ListOptions partitions []partition.Partition ns string expectedCountStmt string @@ -1041,13 +1042,13 @@ func TestConstructQuery(t *testing.T) { var tests []testCase tests = append(tests, testCase{ description: "TestConstructQuery: handles IN statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "queryField1"}, Matches: []string{"somevalue"}, - Op: In, + Op: sqltypes.In, }, }, }, @@ -1066,13 +1067,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles NOT-IN statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "queryField1"}, Matches: []string{"somevalue"}, - Op: NotIn, + Op: sqltypes.NotIn, }, }, }, @@ -1091,12 +1092,12 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles EXISTS statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "queryField1"}, - Op: Exists, + Op: sqltypes.Exists, }, }, }, @@ -1108,12 +1109,12 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles NOT-EXISTS statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "queryField1"}, - Op: NotExists, + Op: sqltypes.NotExists, }, }, }, @@ -1125,13 +1126,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles == statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelEqualFull"}, Matches: []string{"somevalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: false, }, }, @@ -1152,13 +1153,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles == statements for label statements, match partial", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelEqualPartial"}, Matches: []string{"somevalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, }, @@ -1179,13 +1180,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles != statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelNotEqualFull"}, Matches: []string{"somevalue"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: false, }, }, @@ -1210,13 +1211,13 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: handles != statements for label statements, match partial", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelNotEqualPartial"}, Matches: []string{"somevalue"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: true, }, }, @@ -1241,23 +1242,23 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: handles multiple != statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "notEqual1"}, Matches: []string{"value1"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: false, }, }, }, { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "notEqual2"}, Matches: []string{"value2"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: false, }, }, @@ -1286,13 +1287,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles IN statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelIN"}, Matches: []string{"somevalue1", "someValue2"}, - Op: In, + Op: sqltypes.In, }, }, }, @@ -1313,13 +1314,13 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: handles NOTIN statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelNOTIN"}, Matches: []string{"somevalue1", "someValue2"}, - Op: NotIn, + Op: sqltypes.NotIn, }, }, }, @@ -1343,13 +1344,13 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: handles EXISTS statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelEXISTS"}, Matches: []string{}, - Op: Exists, + Op: sqltypes.Exists, }, }, }, @@ -1370,13 +1371,13 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: handles NOTEXISTS statements for label statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelNOTEXISTS"}, Matches: []string{}, - Op: NotExists, + Op: sqltypes.NotExists, }, }, }, @@ -1399,13 +1400,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles LessThan statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "numericThing"}, Matches: []string{"5"}, - Op: Lt, + Op: sqltypes.Lt, }, }, }, @@ -1425,13 +1426,13 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "TestConstructQuery: handles GreaterThan statements", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "numericThing"}, Matches: []string{"35"}, - Op: Gt, + Op: sqltypes.Gt, }, }, }, @@ -1451,19 +1452,19 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "multiple filters with a positive label test and a negative non-label test still outer-join", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "junta"}, Matches: []string{"esther"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, { Field: []string{"metadata", "queryField1"}, Matches: []string{"golgi"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: true, }, }, @@ -1484,33 +1485,33 @@ func TestConstructQuery(t *testing.T) { }) tests = append(tests, testCase{ description: "multiple filters and or-filters with a positive label test and a negative non-label test still outer-join and have correct AND/ORs", - listOptions: ListOptions{Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{ { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "nectar"}, Matches: []string{"stash"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, { Field: []string{"metadata", "queryField1"}, Matches: []string{"landlady"}, - Op: NotEq, + Op: sqltypes.NotEq, Partial: false, }, }, }, { - Filters: []Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "lawn"}, Matches: []string{"reba", "coil"}, - Op: In, + Op: sqltypes.In, }, { Field: []string{"metadata", "queryField1"}, - Op: Gt, + Op: sqltypes.Gt, Matches: []string{"2"}, }, }, @@ -1534,22 +1535,22 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: handles == statements for label statements, match partial, sort on metadata.queryField1", - listOptions: ListOptions{ - Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "labels", "labelEqualPartial"}, Matches: []string{"somevalue"}, - Op: Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, }, - Sort: Sort{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "queryField1"}}, - Orders: []SortOrder{ASC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -1567,10 +1568,10 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "ConstructQuery: sorting when # fields < # sort orders should return an error", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}, {"status", "someotherfield"}}, - Orders: []SortOrder{DESC, ASC, ASC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC, sqltypes.ASC, sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -1582,10 +1583,10 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: sort on label statements with no query", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "labels", "this"}}, - Orders: []SortOrder{ASC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -1603,26 +1604,26 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "TestConstructQuery: sort and query on both labels and non-labels without overlap", - listOptions: ListOptions{ - Filters: []OrFilter{ + listOptions: sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{ { - []Filter{ + []sqltypes.Filter{ { Field: []string{"metadata", "queryField1"}, Matches: []string{"toys"}, - Op: Eq, + Op: sqltypes.Eq, }, { Field: []string{"metadata", "labels", "jamb"}, Matches: []string{"juice"}, - Op: Eq, + Op: sqltypes.Eq, }, }, }, }, - Sort: Sort{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "labels", "this"}, {"status", "queryField2"}}, - Orders: []SortOrder{ASC, DESC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC, sqltypes.DESC}, }, }, partitions: []partition.Partition{}, @@ -1641,10 +1642,10 @@ func TestConstructQuery(t *testing.T) { tests = append(tests, testCase{ description: "ConstructQuery: sorting when # fields > # sort orders should return an error", - listOptions: ListOptions{ - Sort: Sort{ + listOptions: sqltypes.ListOptions{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "somefield"}, {"status", "someotherfield"}, {"metadata", "labels", "a1"}, {"metadata", "labels", "a2"}}, - Orders: []SortOrder{DESC, ASC, ASC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC, sqltypes.ASC, sqltypes.ASC}, }, }, partitions: []partition.Partition{}, @@ -1748,7 +1749,7 @@ func TestBuildSortLabelsClause(t *testing.T) { joinTableIndexByLabelName map[string]int direction bool expectedStmt string - expectedParam string + expectedParam string expectedErr string } diff --git a/pkg/sqlcache/integration_test.go b/pkg/sqlcache/integration_test.go index 69183270..dcc13bbd 100644 --- a/pkg/sqlcache/integration_test.go +++ b/pkg/sqlcache/integration_test.go @@ -18,9 +18,9 @@ import ( "k8s.io/client-go/tools/cache" "sigs.k8s.io/controller-runtime/pkg/envtest" - "github.com/rancher/steve/pkg/sqlcache/informer" "github.com/rancher/steve/pkg/sqlcache/informer/factory" "github.com/rancher/steve/pkg/sqlcache/partition" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" ) const testNamespace = "sql-test" @@ -107,8 +107,8 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { err = i.waitForCacheReady(configMapNames, testNamespace, cache) require.NoError(err) - orFiltersForFilters := func(filters ...informer.Filter) []informer.OrFilter { - return []informer.OrFilter{ + orFiltersForFilters := func(filters ...sqltypes.Filter) []sqltypes.OrFilter { + return []sqltypes.OrFilter{ { Filters: filters, }, @@ -116,85 +116,85 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { } tests := []struct { name string - filters []informer.OrFilter + filters []sqltypes.OrFilter wantNames []string }{ { name: "matches filter", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }), wantNames: []string{"matches-filter"}, }, { name: "partial matches filter", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }), wantNames: []string{"matches-filter", "partial-matches"}, }, { name: "no matches for filter with underscore as it is interpreted literally", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalu_"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }), wantNames: nil, }, { name: "no matches for filter with percent sign as it is interpreted literally", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalu%"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }), wantNames: nil, }, { name: "match with special characters", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"c%%l_value"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }), wantNames: []string{"special-character-matches"}, }, { name: "match with literal backslash character", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{`my\windows\path`}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }), wantNames: []string{"backslash-character-matches"}, }, { name: "not eq filter", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.NotEq, + Op: sqltypes.NotEq, Partial: false, }), wantNames: []string{"partial-matches", "not-matches-filter", "missing", "special-character-matches", "backslash-character-matches"}, }, { name: "partial not eq filter", - filters: orFiltersForFilters(informer.Filter{ + filters: orFiltersForFilters(sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.NotEq, + Op: sqltypes.NotEq, Partial: true, }), wantNames: []string{"not-matches-filter", "missing", "special-character-matches", "backslash-character-matches"}, @@ -202,16 +202,16 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { { name: "multiple or filters match", filters: orFiltersForFilters( - informer.Filter{ + sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, - informer.Filter{ + sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"notequal"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, ), @@ -220,16 +220,16 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { { name: "or filters on different fields", filters: orFiltersForFilters( - informer.Filter{ + sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, - informer.Filter{ + sqltypes.Filter{ Field: []string{`metadata`, `name`}, Matches: []string{"missing"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, ), @@ -237,23 +237,23 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { }, { name: "and filters, both must match", - filters: []informer.OrFilter{ + filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"somevalue"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{`metadata`, `name`}, Matches: []string{"matches-filter"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, @@ -264,10 +264,10 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { { name: "no matches", filters: orFiltersForFilters( - informer.Filter{ + sqltypes.Filter{ Field: []string{"metadata", "annotations", "somekey"}, Matches: []string{"valueNotRepresented"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, ), @@ -278,7 +278,7 @@ func (i *IntegrationSuite) TestSQLCacheFilters() { for _, test := range tests { test := test i.Run(test.name, func() { - options := informer.ListOptions{ + options := sqltypes.ListOptions{ Filters: test.filters, } partitions := []partition.Partition{defaultPartition} @@ -334,7 +334,7 @@ func (i *IntegrationSuite) waitForCacheReady(readyResourceNames []string, namesp ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() return wait.PollUntilContextCancel(ctx, time.Millisecond*100, true, func(ctx context.Context) (done bool, err error) { - var options informer.ListOptions + var options sqltypes.ListOptions partitions := []partition.Partition{defaultPartition} cacheCtx, cacheCancel := context.WithTimeout(ctx, time.Second*5) defer cacheCancel() diff --git a/pkg/sqlcache/informer/listoptions.go b/pkg/sqlcache/sqltypes/types.go similarity index 87% rename from pkg/sqlcache/informer/listoptions.go rename to pkg/sqlcache/sqltypes/types.go index 71bf6f6e..a90a1311 100644 --- a/pkg/sqlcache/informer/listoptions.go +++ b/pkg/sqlcache/sqltypes/types.go @@ -1,4 +1,4 @@ -package informer +package sqltypes type Op string @@ -35,8 +35,8 @@ type ListOptions struct { // Filter represents a field to filter by. // A subfield in an object is represented in a request query using . notation, e.g. 'metadata.name'. // The subfield is internally represented as a slice, e.g. [metadata, name]. -// Complex subfields need to be expressed with square brackets, as in `metadata.labels[zombo.com/moose]`, -// but are mapped to the string slice ["metadata", "labels", "zombo.com/moose"] +// Complex subfields need to be expressed with square brackets, as in `metadata.labels[example.com/moose]`, +// but are mapped to the string slice ["metadata", "labels", "example.com/moose"] // // If more than one value is given for the `Match` field, we do an "IN ()" test type Filter struct { @@ -61,8 +61,18 @@ type Sort struct { Orders []SortOrder } +type SortList struct { + SortDirectives []Sort +} + // Pagination represents how to return paginated results. type Pagination struct { PageSize int Page int } + +func NewSortList() *SortList { + return &SortList{ + SortDirectives: []Sort{}, + } +} diff --git a/pkg/stores/sqlpartition/listprocessor/processor.go b/pkg/stores/sqlpartition/listprocessor/processor.go index f1b89e7c..e60bdc8b 100644 --- a/pkg/stores/sqlpartition/listprocessor/processor.go +++ b/pkg/stores/sqlpartition/listprocessor/processor.go @@ -10,8 +10,8 @@ import ( "github.com/rancher/apiserver/pkg/apierror" "github.com/rancher/apiserver/pkg/types" - "github.com/rancher/steve/pkg/sqlcache/informer" "github.com/rancher/steve/pkg/sqlcache/partition" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" "github.com/rancher/steve/pkg/stores/queryhelper" "github.com/rancher/steve/pkg/stores/sqlpartition/queryparser" "github.com/rancher/steve/pkg/stores/sqlpartition/selection" @@ -36,27 +36,27 @@ const ( ) var endsWithBracket = regexp.MustCompile(`^(.+)\[(.+)]$`) -var mapK8sOpToRancherOp = map[selection.Operator]informer.Op{ - selection.Equals: informer.Eq, - selection.DoubleEquals: informer.Eq, - selection.PartialEquals: informer.Eq, - selection.NotEquals: informer.NotEq, - selection.NotPartialEquals: informer.NotEq, - selection.In: informer.In, - selection.NotIn: informer.NotIn, - selection.Exists: informer.Exists, - selection.DoesNotExist: informer.NotExists, - selection.LessThan: informer.Lt, - selection.GreaterThan: informer.Gt, +var mapK8sOpToRancherOp = map[selection.Operator]sqltypes.Op{ + selection.Equals: sqltypes.Eq, + selection.DoubleEquals: sqltypes.Eq, + selection.PartialEquals: sqltypes.Eq, + selection.NotEquals: sqltypes.NotEq, + selection.NotPartialEquals: sqltypes.NotEq, + selection.In: sqltypes.In, + selection.NotIn: sqltypes.NotIn, + selection.Exists: sqltypes.Exists, + selection.DoesNotExist: sqltypes.NotExists, + selection.LessThan: sqltypes.Lt, + selection.GreaterThan: sqltypes.Gt, } // ListOptions represents the query parameters that may be included in a list request. type ListOptions struct { ChunkSize int Resume string - Filters []informer.OrFilter - Sort informer.Sort - Pagination informer.Pagination + Filters []sqltypes.OrFilter + Sort sqltypes.Sort + Pagination sqltypes.Pagination } type Cache interface { @@ -66,10 +66,10 @@ type Cache interface { // - the total number of resources (returned list might be a subset depending on pagination options in lo) // - a continue token, if there are more pages after the returned one // - an error instead of all of the above if anything went wrong - ListByOptions(ctx context.Context, lo informer.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) + ListByOptions(ctx context.Context, lo sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) } -func k8sOpToRancherOp(k8sOp selection.Operator) (informer.Op, bool, error) { +func k8sOpToRancherOp(k8sOp selection.Operator) (sqltypes.Op, bool, error) { v, ok := mapK8sOpToRancherOp[k8sOp] if ok { return v, k8sOp == selection.PartialEquals || k8sOp == selection.NotPartialEquals, nil @@ -77,11 +77,11 @@ func k8sOpToRancherOp(k8sOp selection.Operator) (informer.Op, bool, error) { return "", false, fmt.Errorf("unknown k8sOp: %s", k8sOp) } -func k8sRequirementToOrFilter(requirement queryparser.Requirement) (informer.Filter, error) { +func k8sRequirementToOrFilter(requirement queryparser.Requirement) (sqltypes.Filter, error) { values := requirement.Values() queryFields := splitQuery(requirement.Key()) op, usePartialMatch, err := k8sOpToRancherOp(requirement.Operator()) - return informer.Filter{ + return sqltypes.Filter{ Field: queryFields, Matches: values, Op: op, @@ -90,8 +90,8 @@ func k8sRequirementToOrFilter(requirement queryparser.Requirement) (informer.Fil } // ParseQuery parses the query params of a request and returns a ListOptions. -func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOptions, error) { - opts := informer.ListOptions{} +func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (sqltypes.ListOptions, error) { + opts := sqltypes.ListOptions{} opts.ChunkSize = getLimit(apiOp) @@ -100,13 +100,13 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOpt opts.Resume = cont filterParams := q[filterParam] - filterOpts := []informer.OrFilter{} + filterOpts := []sqltypes.OrFilter{} for _, filters := range filterParams { requirements, err := queryparser.ParseToRequirements(filters) if err != nil { - return informer.ListOptions{}, err + return sqltypes.ListOptions{}, err } - orFilter := informer.OrFilter{} + orFilter := sqltypes.OrFilter{} for _, requirement := range requirements { filter, err := k8sRequirementToOrFilter(requirement) if err != nil { @@ -118,16 +118,16 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOpt } opts.Filters = filterOpts - sortOpts := informer.Sort{} + sortOpts := sqltypes.Sort{} sortKeys := q.Get(sortParam) if sortKeys != "" { sortParts := strings.Split(sortKeys, ",") for _, sortPart := range sortParts { field := sortPart if len(field) > 0 { - sortOrder := informer.ASC + sortOrder := sqltypes.ASC if field[0] == '-' { - sortOrder = informer.DESC + sortOrder = sqltypes.DESC field = field[1:] } if len(field) > 0 { @@ -140,7 +140,7 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOpt opts.Sort = sortOpts var err error - pagination := informer.Pagination{} + pagination := sqltypes.Pagination{} pagination.PageSize, err = strconv.Atoi(q.Get(pageSizeParam)) if err != nil { pagination.PageSize = 0 @@ -151,12 +151,12 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOpt } opts.Pagination = pagination - op := informer.Eq + op := sqltypes.Eq projectsOrNamespaces := q.Get(projectsOrNamespacesVar) if projectsOrNamespaces == "" { projectsOrNamespaces = q.Get(projectsOrNamespacesVar + notOp) if projectsOrNamespaces != "" { - op = informer.NotEq + op = sqltypes.NotEq } } if projectsOrNamespaces != "" { @@ -167,12 +167,12 @@ func ParseQuery(apiOp *types.APIRequest, namespaceCache Cache) (informer.ListOpt 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 op == informer.NotEq { + if op == sqltypes.NotEq { for _, filter := range projOrNSFilters { - opts.Filters = append(opts.Filters, informer.OrFilter{Filters: []informer.Filter{filter}}) + opts.Filters = append(opts.Filters, sqltypes.OrFilter{Filters: []sqltypes.Filter{filter}}) } } else { - opts.Filters = append(opts.Filters, informer.OrFilter{Filters: projOrNSFilters}) + opts.Filters = append(opts.Filters, sqltypes.OrFilter{Filters: projOrNSFilters}) } } @@ -205,22 +205,22 @@ func splitQuery(query string) []string { return strings.Split(query, ".") } -func parseNamespaceOrProjectFilters(ctx context.Context, projOrNS string, op informer.Op, namespaceInformer Cache) ([]informer.Filter, error) { - var filters []informer.Filter +func parseNamespaceOrProjectFilters(ctx context.Context, projOrNS string, op sqltypes.Op, namespaceInformer Cache) ([]sqltypes.Filter, error) { + var filters []sqltypes.Filter for _, pn := range strings.Split(projOrNS, ",") { - uList, _, _, err := namespaceInformer.ListByOptions(ctx, informer.ListOptions{ - Filters: []informer.OrFilter{ + uList, _, _, err := namespaceInformer.ListByOptions(ctx, sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "name"}, Matches: []string{pn}, - Op: informer.Eq, + Op: sqltypes.Eq, }, { Field: []string{"metadata", "labels", "field.cattle.io/projectId"}, Matches: []string{pn}, - Op: informer.Eq, + Op: sqltypes.Eq, }, }, }, @@ -230,7 +230,7 @@ func parseNamespaceOrProjectFilters(ctx context.Context, projOrNS string, op inf return filters, err } for _, item := range uList.Items { - filters = append(filters, informer.Filter{ + filters = append(filters, sqltypes.Filter{ Field: []string{"metadata", "namespace"}, Matches: []string{item.GetName()}, Op: op, diff --git a/pkg/stores/sqlpartition/listprocessor/processor_test.go b/pkg/stores/sqlpartition/listprocessor/processor_test.go index 0674f82b..59280401 100644 --- a/pkg/stores/sqlpartition/listprocessor/processor_test.go +++ b/pkg/stores/sqlpartition/listprocessor/processor_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/rancher/apiserver/pkg/types" - "github.com/rancher/steve/pkg/sqlcache/informer" "github.com/rancher/steve/pkg/sqlcache/partition" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -23,7 +23,7 @@ func TestParseQuery(t *testing.T) { setupNSCache func() Cache nsc Cache req *types.APIRequest - expectedLO informer.ListOptions + expectedLO sqltypes.ListOptions errExpected bool errorText string } @@ -35,10 +35,10 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: ""}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -51,21 +51,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "projectsornamespaces=somethin"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "namespace"}, Matches: []string{"ns1"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -82,19 +82,19 @@ func TestParseQuery(t *testing.T) { }, } nsc := NewMockCache(gomock.NewController(t)) - nsc.EXPECT().ListByOptions(context.Background(), informer.ListOptions{ - Filters: []informer.OrFilter{ + nsc.EXPECT().ListByOptions(context.Background(), sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "name"}, Matches: []string{"somethin"}, - Op: informer.Eq, + Op: sqltypes.Eq, }, { Field: []string{"metadata", "labels", "field.cattle.io/projectId"}, Matches: []string{"somethin"}, - Op: informer.Eq, + Op: sqltypes.Eq, }, }, }, @@ -111,40 +111,40 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "projectsornamespaces=somethin"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "namespace"}, Matches: []string{"ns1"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, errExpected: true, setupNSCache: func() Cache { nsi := NewMockCache(gomock.NewController(t)) - nsi.EXPECT().ListByOptions(context.Background(), informer.ListOptions{ - Filters: []informer.OrFilter{ + nsi.EXPECT().ListByOptions(context.Background(), sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "name"}, Matches: []string{"somethin"}, - Op: informer.Eq, + Op: sqltypes.Eq, }, { Field: []string{"metadata", "labels", "field.cattle.io/projectId"}, Matches: []string{"somethin"}, - Op: informer.Eq, + Op: sqltypes.Eq, }, }, }, @@ -161,21 +161,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "projectsornamespaces=somethin"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "namespace"}, Matches: []string{"ns1"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -185,19 +185,19 @@ func TestParseQuery(t *testing.T) { Items: []unstructured.Unstructured{}, } nsi := NewMockCache(gomock.NewController(t)) - nsi.EXPECT().ListByOptions(context.Background(), informer.ListOptions{ - Filters: []informer.OrFilter{ + nsi.EXPECT().ListByOptions(context.Background(), sqltypes.ListOptions{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "name"}, Matches: []string{"somethin"}, - Op: informer.Eq, + Op: sqltypes.Eq, }, { Field: []string{"metadata", "labels", "field.cattle.io/projectId"}, Matches: []string{"somethin"}, - Op: informer.Eq, + Op: sqltypes.Eq, }, }, }, @@ -213,21 +213,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a~c"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a"}, Matches: []string{"c"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -239,21 +239,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a=c"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a"}, Matches: []string{"c"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -274,21 +274,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=metadata.labels[grover.example.com/fish]~heads"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "grover.example.com/fish"}, Matches: []string{"heads"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -300,21 +300,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=metadata.annotations[chumley.example.com/fish]=seals"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "annotations", "chumley.example.com/fish"}, Matches: []string{"seals"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -326,20 +326,20 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=metadata.fields[3]<5"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "fields", "3"}, Matches: []string{"5"}, - Op: informer.Lt, + Op: sqltypes.Lt, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -351,21 +351,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=metadata.labels[grover.example.com/fish]~heads"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "grover.example.com/fish"}, Matches: []string{"heads"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -377,31 +377,31 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a=c&filter=b=d"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a"}, Matches: []string{"c"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"b"}, Matches: []string{"d"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -413,31 +413,31 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a=c&filter=b=d"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a"}, Matches: []string{"c"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"b"}, Matches: []string{"d"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -449,27 +449,27 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=beer=pabst,metadata.labels[beer2.io/ale] ~schlitz"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"beer"}, Matches: []string{"pabst"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, { Field: []string{"metadata", "labels", "beer2.io/ale"}, Matches: []string{"schlitz"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -481,27 +481,27 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=beer=natty-bo,metadata.labels.beer3~rainier"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"beer"}, Matches: []string{"natty-bo"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: false, }, { Field: []string{"metadata", "labels", "beer3"}, Matches: []string{"rainier"}, - Op: informer.Eq, + Op: sqltypes.Eq, Partial: true, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -513,27 +513,27 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a1In in (x1),a2In IN (x2)"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a1In"}, Matches: []string{"x1"}, - Op: informer.In, + Op: sqltypes.In, Partial: false, }, { Field: []string{"a2In"}, Matches: []string{"x2"}, - Op: informer.In, + Op: sqltypes.In, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -545,21 +545,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a2In in (x2a, x2b)"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a2In"}, Matches: []string{"x2a", "x2b"}, - Op: informer.In, + Op: sqltypes.In, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -571,27 +571,27 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a1NotIn notin (x1),a2NotIn NOTIN (x2)"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a1NotIn"}, Matches: []string{"x1"}, - Op: informer.NotIn, + Op: sqltypes.NotIn, Partial: false, }, { Field: []string{"a2NotIn"}, Matches: []string{"x2"}, - Op: informer.NotIn, + Op: sqltypes.NotIn, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -603,21 +603,21 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a3NotIn in (x3a, x3b)"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a3NotIn"}, Matches: []string{"x3a", "x3b"}, - Op: informer.In, + Op: sqltypes.In, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -629,27 +629,27 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a4In iN (x4a),a4NotIn nOtIn (x4b)"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a4In"}, Matches: []string{"x4a"}, - Op: informer.In, + Op: sqltypes.In, Partial: false, }, { Field: []string{"a4NotIn"}, Matches: []string{"x4b"}, - Op: informer.NotIn, + Op: sqltypes.NotIn, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -671,33 +671,33 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=metadata.labels.a5In1,!metadata.labels.a5In2, ! metadata.labels.a5In3"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"metadata", "labels", "a5In1"}, - Op: informer.Exists, + Op: sqltypes.Exists, Matches: []string{}, Partial: false, }, { Field: []string{"metadata", "labels", "a5In2"}, - Op: informer.NotExists, + Op: sqltypes.NotExists, Matches: []string{}, Partial: false, }, { Field: []string{"metadata", "labels", "a5In3"}, - Op: informer.NotExists, + Op: sqltypes.NotExists, Matches: []string{}, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -709,27 +709,27 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "filter=a<1,b>2"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: []informer.OrFilter{ + Filters: []sqltypes.OrFilter{ { - Filters: []informer.Filter{ + Filters: []sqltypes.Filter{ { Field: []string{"a"}, - Op: informer.Lt, + Op: sqltypes.Lt, Matches: []string{"1"}, Partial: false, }, { Field: []string{"b"}, - Op: informer.Gt, + Op: sqltypes.Gt, Matches: []string{"2"}, Partial: false, }, }, }, }, - Pagination: informer.Pagination{ + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -742,15 +742,15 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "sort=metadata.name"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Sort: informer.Sort{ + Sort: sqltypes.Sort{ Fields: [][]string{ {"metadata", "name"}}, - Orders: []informer.SortOrder{informer.ASC}, + Orders: []sqltypes.SortOrder{sqltypes.ASC}, }, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -763,14 +763,14 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "sort=-metadata.name"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Sort: informer.Sort{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "name"}}, - Orders: []informer.SortOrder{informer.DESC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC}, }, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -783,20 +783,20 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "sort=-metadata.name,spec.something"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Sort: informer.Sort{ + Sort: sqltypes.Sort{ Fields: [][]string{ {"metadata", "name"}, {"spec", "something"}, }, - Orders: []informer.SortOrder{ - informer.DESC, - informer.ASC, + Orders: []sqltypes.SortOrder{ + sqltypes.DESC, + sqltypes.ASC, }, }, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -809,17 +809,17 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "sort=-metadata.labels[beef.cattle.io/snort],metadata.labels.steer,metadata.labels[bossie.cattle.io/moo],spec.something"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Sort: informer.Sort{ + Sort: sqltypes.Sort{ Fields: [][]string{{"metadata", "labels", "beef.cattle.io/snort"}, {"metadata", "labels", "steer"}, {"metadata", "labels", "bossie.cattle.io/moo"}, {"spec", "something"}}, - Orders: []informer.SortOrder{informer.DESC, informer.ASC, informer.ASC, informer.ASC}, + Orders: []sqltypes.SortOrder{sqltypes.DESC, sqltypes.ASC, sqltypes.ASC, sqltypes.ASC}, }, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -835,11 +835,11 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "continue=5"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, Resume: "5", - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -852,11 +852,11 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "continue=5"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, Resume: "5", - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -869,10 +869,10 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "limit=3"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: 3, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 1, }, }, @@ -885,10 +885,10 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "page=3"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ Page: 3, }, }, @@ -901,10 +901,10 @@ func TestParseQuery(t *testing.T) { URL: &url.URL{RawQuery: "pagesize=20"}, }, }, - expectedLO: informer.ListOptions{ + expectedLO: sqltypes.ListOptions{ ChunkSize: defaultLimit, - Filters: make([]informer.OrFilter, 0), - Pagination: informer.Pagination{ + Filters: make([]sqltypes.OrFilter, 0), + Pagination: sqltypes.Pagination{ PageSize: 20, Page: 1, }, diff --git a/pkg/stores/sqlpartition/listprocessor/proxy_mocks_test.go b/pkg/stores/sqlpartition/listprocessor/proxy_mocks_test.go index 06598043..04a5cfb5 100644 --- a/pkg/stores/sqlpartition/listprocessor/proxy_mocks_test.go +++ b/pkg/stores/sqlpartition/listprocessor/proxy_mocks_test.go @@ -13,8 +13,8 @@ import ( context "context" reflect "reflect" - informer "github.com/rancher/steve/pkg/sqlcache/informer" partition "github.com/rancher/steve/pkg/sqlcache/partition" + sqltypes "github.com/rancher/steve/pkg/sqlcache/sqltypes" gomock "go.uber.org/mock/gomock" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -43,7 +43,7 @@ func (m *MockCache) EXPECT() *MockCacheMockRecorder { } // ListByOptions mocks base method. -func (m *MockCache) ListByOptions(arg0 context.Context, arg1 informer.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { +func (m *MockCache) ListByOptions(arg0 context.Context, arg1 sqltypes.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListByOptions", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*unstructured.UnstructuredList) diff --git a/pkg/stores/sqlproxy/proxy_mocks_test.go b/pkg/stores/sqlproxy/proxy_mocks_test.go index 3aeee7e5..8061bc84 100644 --- a/pkg/stores/sqlproxy/proxy_mocks_test.go +++ b/pkg/stores/sqlproxy/proxy_mocks_test.go @@ -14,9 +14,9 @@ import ( reflect "reflect" types "github.com/rancher/apiserver/pkg/types" - informer "github.com/rancher/steve/pkg/sqlcache/informer" factory "github.com/rancher/steve/pkg/sqlcache/informer/factory" partition "github.com/rancher/steve/pkg/sqlcache/partition" + sqltypes "github.com/rancher/steve/pkg/sqlcache/sqltypes" summary "github.com/rancher/wrangler/v3/pkg/summary" gomock "go.uber.org/mock/gomock" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -51,7 +51,7 @@ func (m *MockCache) EXPECT() *MockCacheMockRecorder { } // ListByOptions mocks base method. -func (m *MockCache) ListByOptions(arg0 context.Context, arg1 informer.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { +func (m *MockCache) ListByOptions(arg0 context.Context, arg1 sqltypes.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListByOptions", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*unstructured.UnstructuredList) diff --git a/pkg/stores/sqlproxy/proxy_store.go b/pkg/stores/sqlproxy/proxy_store.go index 96e03986..3ffe751c 100644 --- a/pkg/stores/sqlproxy/proxy_store.go +++ b/pkg/stores/sqlproxy/proxy_store.go @@ -36,6 +36,7 @@ import ( "github.com/rancher/steve/pkg/sqlcache/informer" "github.com/rancher/steve/pkg/sqlcache/informer/factory" "github.com/rancher/steve/pkg/sqlcache/partition" + "github.com/rancher/steve/pkg/sqlcache/sqltypes" "github.com/rancher/wrangler/v3/pkg/data" "github.com/rancher/wrangler/v3/pkg/schemas" "github.com/rancher/wrangler/v3/pkg/schemas/validation" @@ -216,7 +217,7 @@ type Cache interface { // - the total number of resources (returned list might be a subset depending on pagination options in lo) // - a continue token, if there are more pages after the returned one // - an error instead of all of the above if anything went wrong - ListByOptions(ctx context.Context, lo informer.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) + ListByOptions(ctx context.Context, lo sqltypes.ListOptions, partitions []partition.Partition, namespace string) (*unstructured.UnstructuredList, int, string, error) } // WarningBuffer holds warnings that may be returned from the kubernetes api diff --git a/pkg/stores/sqlproxy/sql_informer_mocks_test.go b/pkg/stores/sqlproxy/sql_informer_mocks_test.go index 125f2192..2a51e2b3 100644 --- a/pkg/stores/sqlproxy/sql_informer_mocks_test.go +++ b/pkg/stores/sqlproxy/sql_informer_mocks_test.go @@ -13,8 +13,8 @@ import ( context "context" reflect "reflect" - informer "github.com/rancher/steve/pkg/sqlcache/informer" partition "github.com/rancher/steve/pkg/sqlcache/partition" + sqltypes "github.com/rancher/steve/pkg/sqlcache/sqltypes" gomock "go.uber.org/mock/gomock" unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -43,7 +43,7 @@ func (m *MockByOptionsLister) EXPECT() *MockByOptionsListerMockRecorder { } // ListByOptions mocks base method. -func (m *MockByOptionsLister) ListByOptions(arg0 context.Context, arg1 informer.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { +func (m *MockByOptionsLister) ListByOptions(arg0 context.Context, arg1 sqltypes.ListOptions, arg2 []partition.Partition, arg3 string) (*unstructured.UnstructuredList, int, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ListByOptions", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(*unstructured.UnstructuredList)