mirror of
https://github.com/rancher/steve.git
synced 2025-06-29 08:17:20 +00:00
* Add more fields to index when sql-caching is on. * Restore the gvkKey helper, add event fields. The UI team wasn't sure whether the event fields should go in the empty-string group or in 'events.k8s.io', so let's go with both until/unless specified otherwise. * More fixes to the fields to index: - Remove the erroneously added management.cattle.io.nodes fields - Use the builtin Event class, not events.k8s.io (by looking at the dashboard client code) * Start on the virtual-field work. * Map `Event.type` to `Event._type` for indexing. * Add a unit test for field replacement for Event.type * Add label processing. * Don't test for transformation of event objects in the common module. * Parse metadata.label queries differently. * Improve a variable name that turned out to not be temporary. * No need to specifically cache certain labels, as all are now cached. * Add a test to verify simple label (m.labels.foo=blah) queries work. * 'addLabelFields' never returns an error. * Delete superseded function. * Was calling 'addLabelFields' one time too many. * Start using k8s ParseToRequirements * Pull in the k8s parser. * Successfully test for quotation marks. * Add quoted strings to the lexer. * Move to a forked k8s label lexer to include non-label tests. * Improve and test the way quoted strings in the query are detected. * Reinstate the original Apache license in the derived code. Following clause 4.3 of the Apache license: "You must cause any modified files to carry prominent notices stating that You changed the files..." * Ignore case for operators. * Test IN multiple-target-values * Test the not-in operator. * Ignore case for operators. SQL is case-insensitive on field names and values, so this just adds consistency. * Added tests for parsing EXISTS and NOT-EXISTS queries. * Parse less-than and greater-than ops * Lasso's `CacheFor` now takes a `watchable` argument. * Support 'gt' and 'lt' as synonyms for '<' and '>'. I see both types of operators being bandied about -- it's easy to support the aliases. * typo fix * Have the filter parser allow exist tests only on labels. Also reduce the case where there's no namespace function. * Specify hard-wired fields to index alphabetically. * Remove unused variable. * Parser: 'metadata.labels[FIELD]' is valid * Pull in new gvk fields from main (and keep in alpha order). * Fixed a couple of drops done during the last rebase. * Add a reminder to keep the entries in alpha order. * Test TransformLabels * Remove TransformLabels * Remove unused/unneeded code. * Describe diffs between our label-selector parser and upstream's. * Use the merged lasso 46333 work. * Drop unused field. * Tighten up the code. * Specify which commit the label selector parser is based on. * Allow both single-quoted and double-quoted value matching, doc difference. * More review-driven changes: - Stricter processing of m.l.name keys: Require ending close-bracket for a start-bracket - Comment fix - Moving sql processing from lasso to steve: some changes missed in rebase * Drop support for double-quotes for string values. For now on only single-quotes (or none where possible) are allowed. * Renaming and dropping an init block. * Quoted strings are dropped from the filter queries In particular, label values have a specific syntax: they must start and end with a letter, and their innards may contain only alnums '.', '-' and '_'. So there's no need for quoting. And that means now that `=` and `==` do exact matches, and the `~` operator does a partial match. `!=` and `!~` negate -- note that `!~` is a stricter operation than `!=`, in that given a set of possible string values, `!=` will accept more of them than `!~`. Maybe I shouldn't have gone here, but these operators reminded me of learning about `nicht durfen` and `nicht sollen` in German, or something like that. * Move a constant definition to the module level. * Remove commented-out code. * Remove unused func and adjacent redundant comment.
167 lines
4.4 KiB
Go
167 lines
4.4 KiB
Go
/*
|
|
Copyright 2014 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
/*
|
|
This file is derived from
|
|
https://github.com/kubernetes/apimachinery/blob/90df4d1d2d40ea9b3a522bec6e3577237358de00/pkg/labels/labels.go
|
|
- FormatLabels was dropped
|
|
- validateLabelKey calls from ConvertSelectorToLabelsMap were dropped
|
|
*/
|
|
|
|
package queryparser
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
)
|
|
|
|
// Labels allows you to present labels independently from their storage.
|
|
type Labels interface {
|
|
// Has returns whether the provided label exists.
|
|
Has(label string) (exists bool)
|
|
|
|
// Get returns the value for the provided label.
|
|
Get(label string) (value string)
|
|
}
|
|
|
|
// Set is a map of label:value. It implements Labels.
|
|
type Set map[string]string
|
|
|
|
// String returns all labels listed as a human readable string.
|
|
// Conveniently, exactly the format that ParseSelector takes.
|
|
func (ls Set) String() string {
|
|
selector := make([]string, 0, len(ls))
|
|
for key, value := range ls {
|
|
selector = append(selector, key+"="+value)
|
|
}
|
|
// Sort for determinism.
|
|
sort.StringSlice(selector).Sort()
|
|
return strings.Join(selector, ",")
|
|
}
|
|
|
|
// Has returns whether the provided label exists in the map.
|
|
func (ls Set) Has(label string) bool {
|
|
_, exists := ls[label]
|
|
return exists
|
|
}
|
|
|
|
// Get returns the value in the map for the provided label.
|
|
func (ls Set) Get(label string) string {
|
|
return ls[label]
|
|
}
|
|
|
|
// AsSelector converts labels into a selectors. It does not
|
|
// perform any validation, which means the server will reject
|
|
// the request if the Set contains invalid values.
|
|
func (ls Set) AsSelector() Selector {
|
|
return SelectorFromSet(ls)
|
|
}
|
|
|
|
// AsValidatedSelector converts labels into a selectors.
|
|
// The Set is validated client-side, which allows to catch errors early.
|
|
func (ls Set) AsValidatedSelector() (Selector, error) {
|
|
return ValidatedSelectorFromSet(ls)
|
|
}
|
|
|
|
// AsSelectorPreValidated converts labels into a selector, but
|
|
// assumes that labels are already validated and thus doesn't
|
|
// perform any validation.
|
|
// According to our measurements this is significantly faster
|
|
// in codepaths that matter at high scale.
|
|
// Note: this method copies the Set; if the Set is immutable, consider wrapping it with ValidatedSetSelector
|
|
// instead, which does not copy.
|
|
func (ls Set) AsSelectorPreValidated() Selector {
|
|
return SelectorFromValidatedSet(ls)
|
|
}
|
|
|
|
// Conflicts takes 2 maps and returns true if there a key match between
|
|
// the maps but the value doesn't match, and returns false in other cases
|
|
func Conflicts(labels1, labels2 Set) bool {
|
|
small := labels1
|
|
big := labels2
|
|
if len(labels2) < len(labels1) {
|
|
small = labels2
|
|
big = labels1
|
|
}
|
|
|
|
for k, v := range small {
|
|
if val, match := big[k]; match {
|
|
if val != v {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Merge combines given maps, and does not check for any conflicts
|
|
// between the maps. In case of conflicts, second map (labels2) wins
|
|
func Merge(labels1, labels2 Set) Set {
|
|
mergedMap := Set{}
|
|
|
|
for k, v := range labels1 {
|
|
mergedMap[k] = v
|
|
}
|
|
for k, v := range labels2 {
|
|
mergedMap[k] = v
|
|
}
|
|
return mergedMap
|
|
}
|
|
|
|
// Equals returns true if the given maps are equal
|
|
func Equals(labels1, labels2 Set) bool {
|
|
if len(labels1) != len(labels2) {
|
|
return false
|
|
}
|
|
|
|
for k, v := range labels1 {
|
|
value, ok := labels2[k]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if value != v {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ConvertSelectorToLabelsMap converts selector string to labels map
|
|
// and validates keys and values
|
|
func ConvertSelectorToLabelsMap(selector string, opts ...field.PathOption) (Set, error) {
|
|
labelsMap := Set{}
|
|
|
|
if len(selector) == 0 {
|
|
return labelsMap, nil
|
|
}
|
|
|
|
labels := strings.Split(selector, ",")
|
|
for _, label := range labels {
|
|
l := strings.Split(label, "=")
|
|
if len(l) != 2 {
|
|
return labelsMap, fmt.Errorf("invalid selector: %s", l)
|
|
}
|
|
key := strings.TrimSpace(l[0])
|
|
value := strings.TrimSpace(l[1])
|
|
labelsMap[key] = value
|
|
}
|
|
return labelsMap, nil
|
|
}
|