mirror of
https://github.com/rancher/steve.git
synced 2025-09-05 01:12:09 +00:00
Generate field names with brackets when needed. (#477)
* Generate field names with brackets when needed. * Stop hard-wiring complex selectors as `["field1", "field2[sub-field3]"]` and instead represent them as a more consistent `["field1", "field2", "sub-field3"]` * Convert all filter strings ending with square brackets to an array. Stop special-casing 'metadata.labels[X]' and handle any query string that ends with '[...]'. * Stop checking for pre-bracketed terms in constant field-accessor arrays. In this commit we stop converting string arrays like `["metadata", "labels[k8s.io/deepcode]"]` into the database field `"metadata.labels[k8s.io/deepcode]"` and instead will do a naive `join` to give `metadata[labels[k8s.io/deepcode]]`. The solution is to never express the above terms in separate fields, like `["metadata", "labels", "k8s.io/deepcode"]`. This also better reflects the stucture of the referenced object. * gofmt changes * Simplify comment about 'smartJoin'.
This commit is contained in:
@@ -35,7 +35,7 @@ const (
|
||||
notOp = "!"
|
||||
)
|
||||
|
||||
var labelsRegex = regexp.MustCompile(`^(metadata)\.(labels)\[(.+)\]$`)
|
||||
var endsWithBracket = regexp.MustCompile(`^(.+)\[(.+)]$`)
|
||||
var mapK8sOpToRancherOp = map[selection.Operator]informer.Op{
|
||||
selection.Equals: informer.Eq,
|
||||
selection.DoubleEquals: informer.Eq,
|
||||
@@ -191,17 +191,16 @@ func getLimit(apiOp *types.APIRequest) int {
|
||||
return limit
|
||||
}
|
||||
|
||||
// splitQuery takes a single-string metadata-labels filter and converts it into an array of 3 accessor strings,
|
||||
// where the first two strings are always "metadata" and "labels", and the third is the label name.
|
||||
// This is more complex than doing something like `strings.Split(".", "metadata.labels.fieldName")
|
||||
// because the fieldName can be more complex - in particular it can contain "."s) and needs to be
|
||||
// bracketed, as in `metadata.labels[rancher.io/cattle.and.beef]".
|
||||
// The `labelsRegex` looks for the bracketed form.
|
||||
// splitQuery takes a single-string k8s object accessor and returns its separate fields in a slice.
|
||||
// "Simple" accessors of the form `metadata.labels.foo` => ["metadata", "labels", "foo"]
|
||||
// but accessors with square brackets need to be broken on the brackets, as in
|
||||
// "metadata.annotations[k8s.io/this-is-fun]" => ["metadata", "annotations", "k8s.io/this-is-fun"]
|
||||
// We assume in the kubernetes/rancher world json keys are always alphanumeric-underscorish, so
|
||||
// we only look for square brackets at the end of the string.
|
||||
func splitQuery(query string) []string {
|
||||
m := labelsRegex.FindStringSubmatch(query)
|
||||
if m != nil && len(m) == 4 {
|
||||
// m[0] contains the entire string, so just return all but that first item in `m`
|
||||
return m[1:]
|
||||
m := endsWithBracket.FindStringSubmatch(query)
|
||||
if m != nil {
|
||||
return append(strings.Split(m[1], "."), m[2])
|
||||
}
|
||||
return strings.Split(query, ".")
|
||||
}
|
||||
@@ -219,7 +218,7 @@ func parseNamespaceOrProjectFilters(ctx context.Context, projOrNS string, op inf
|
||||
Op: informer.Eq,
|
||||
},
|
||||
{
|
||||
Field: []string{"metadata", "labels[field.cattle.io/projectId]"},
|
||||
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||
Matches: []string{pn},
|
||||
Op: informer.Eq,
|
||||
},
|
||||
|
@@ -92,7 +92,7 @@ func TestParseQuery(t *testing.T) {
|
||||
Op: informer.Eq,
|
||||
},
|
||||
{
|
||||
Field: []string{"metadata", "labels[field.cattle.io/projectId]"},
|
||||
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||
Matches: []string{"somethin"},
|
||||
Op: informer.Eq,
|
||||
},
|
||||
@@ -142,7 +142,7 @@ func TestParseQuery(t *testing.T) {
|
||||
Op: informer.Eq,
|
||||
},
|
||||
{
|
||||
Field: []string{"metadata", "labels[field.cattle.io/projectId]"},
|
||||
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||
Matches: []string{"somethin"},
|
||||
Op: informer.Eq,
|
||||
},
|
||||
@@ -195,7 +195,7 @@ func TestParseQuery(t *testing.T) {
|
||||
Op: informer.Eq,
|
||||
},
|
||||
{
|
||||
Field: []string{"metadata", "labels[field.cattle.io/projectId]"},
|
||||
Field: []string{"metadata", "labels", "field.cattle.io/projectId"},
|
||||
Matches: []string{"somethin"},
|
||||
Op: informer.Eq,
|
||||
},
|
||||
@@ -293,6 +293,83 @@ func TestParseQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
tests = append(tests, testCase{
|
||||
description: "ParseQuery() with an annotations filter param should split it correctly.",
|
||||
req: &types.APIRequest{
|
||||
Request: &http.Request{
|
||||
URL: &url.URL{RawQuery: "filter=metadata.annotations[chumley.example.com/fish]=seals"},
|
||||
},
|
||||
},
|
||||
expectedLO: informer.ListOptions{
|
||||
ChunkSize: defaultLimit,
|
||||
Filters: []informer.OrFilter{
|
||||
{
|
||||
Filters: []informer.Filter{
|
||||
{
|
||||
Field: []string{"metadata", "annotations", "chumley.example.com/fish"},
|
||||
Matches: []string{"seals"},
|
||||
Op: informer.Eq,
|
||||
Partial: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Pagination: informer.Pagination{
|
||||
Page: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
tests = append(tests, testCase{
|
||||
description: "ParseQuery() with a numeric filter index should split it correctly.",
|
||||
req: &types.APIRequest{
|
||||
Request: &http.Request{
|
||||
URL: &url.URL{RawQuery: "filter=metadata.fields[3]<5"},
|
||||
},
|
||||
},
|
||||
expectedLO: informer.ListOptions{
|
||||
ChunkSize: defaultLimit,
|
||||
Filters: []informer.OrFilter{
|
||||
{
|
||||
Filters: []informer.Filter{
|
||||
{
|
||||
Field: []string{"metadata", "fields", "3"},
|
||||
Matches: []string{"5"},
|
||||
Op: informer.Lt,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Pagination: informer.Pagination{
|
||||
Page: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
tests = append(tests, testCase{
|
||||
description: "ParseQuery() with a labels filter param should create a labels-specific filter.",
|
||||
req: &types.APIRequest{
|
||||
Request: &http.Request{
|
||||
URL: &url.URL{RawQuery: "filter=metadata.labels[grover.example.com/fish]~heads"},
|
||||
},
|
||||
},
|
||||
expectedLO: informer.ListOptions{
|
||||
ChunkSize: defaultLimit,
|
||||
Filters: []informer.OrFilter{
|
||||
{
|
||||
Filters: []informer.Filter{
|
||||
{
|
||||
Field: []string{"metadata", "labels", "grover.example.com/fish"},
|
||||
Matches: []string{"heads"},
|
||||
Op: informer.Eq,
|
||||
Partial: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Pagination: informer.Pagination{
|
||||
Page: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
tests = append(tests, testCase{
|
||||
description: "ParseQuery() with multiple filter params, should include multiple or filters.",
|
||||
req: &types.APIRequest{
|
||||
|
Reference in New Issue
Block a user