Skip to content

Commit

Permalink
SQLite backed cache: Support sorting mgmt clusters on value in a spe…
Browse files Browse the repository at this point in the history
…cific condition (#447)

* Replace primary/secondary sort fields with an array of sort directives.

* Allow more than 2 sort-params in a search query.

* Add a virtual 'status.ready' field to clusters.

* Rename status.ready -> status.connected

* Set virtual field 'spec.internal' <- spec.displayName == 'local'

* Need to declare all virtual fields to index.

* Ready clusters have condition[type==Ready && status=True]

* Update the README to reflect generalized sorting.

* Bump lasso to get revised sort directives.

* Review-driven changes, mostly comments and drop unneeded code.

* Add unit tests to verify sort-order stringification.

* Ignore empty-string sort components.

* Fix a rebase mishap.

* Drop unneeded commented-out code.

* Clusters have a 'spec.internal' field, no need to synthesize one.

* Added a note on square-brackets for label references.

This should be added to the README for filter queries in the PR for 46333.

* Bump to latest sqlcache-free lasso
  • Loading branch information
ericpromislow authored Jan 27, 2025
1 parent 809e927 commit c180569
Show file tree
Hide file tree
Showing 14 changed files with 709 additions and 99 deletions.
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,9 @@ The list can be negated to exclude results:

#### `sort`

Results can be sorted lexicographically by primary and secondary columns.
Results can be sorted lexicographically by any number of columns given in descending order of importance.

Sorting by only a primary column, for example name:
Sorting by only a single column, for example name:

```
/v1/{type}?sort=metadata.name
Expand All @@ -201,30 +201,38 @@ Reverse sorting by name:
/v1/{type}?sort=-metadata.name
```

The secondary sort criteria is comma separated.
Multiple sort criteria are comma separated.

Example, sorting by name and creation time in ascending order:
Example, sorting first by name and then by creation time in ascending order:

```
/v1/{type}?sort=metadata.name,metadata.creationTimestamp
```

Reverse sort by name, normal sort by creation time:
Reverse sort by name, then normal sort by creation time:

```
/v1/{type}?sort=-metadata.name,metadata.creationTimestamp
```

Normal sort by name, reverse sort by creation time:
Normal sort by namespace, then by name, reverse sort by creation time:

```
/v1/{type}?sort=metadata.name,-metadata.creationTimestamp
/v1/{type}?sort=metadata.namespace,metadata.name,-metadata.creationTimestamp
```

**If SQLite caching is enabled** (`server.Options.SQLCache=true`),
sorting is only supported for the set of attributes supported by
filtering (see above).

Sorting by labels (also requires SQLite caching) can use complex label names.
This query sorts by app name within their architectures, with the architectures
listed in reverse lexicographic order. Note that complex label names need to be
surrounded by square brackets (which themselves need to be percent-escaped for some web queries)

```
/v1/nodes?sort=-metadata.labels[kubernetes.io/arch],metadata.name
```

#### `page`, `pagesize`, and `revision`

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/rancher/apiserver v0.0.0-20241009200134-5a4ecca7b988
github.com/rancher/dynamiclistener v0.6.1-rc.2
github.com/rancher/kubernetes-provider-detector v0.1.5
github.com/rancher/lasso v0.0.0-20241202185148-04649f379358
github.com/rancher/lasso v0.0.0-20250123080302-9325fed68518
github.com/rancher/norman v0.0.0-20241001183610-78a520c160ab
github.com/rancher/remotedialer v0.3.2
github.com/rancher/wrangler/v3 v3.0.1-rc.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ github.com/rancher/dynamiclistener v0.6.1-rc.2 h1:PTKNKcYXZjc/lo40EivRcXuEbCXwjp
github.com/rancher/dynamiclistener v0.6.1-rc.2/go.mod h1:0KhUMHy3VcGMGavTY3i1/Mr8rVM02wFqNlUzjc+Cplg=
github.com/rancher/kubernetes-provider-detector v0.1.5 h1:hWRAsWuJOemzGjz/XrbTlM7QmfO4OedvFE3QwXiH60I=
github.com/rancher/kubernetes-provider-detector v0.1.5/go.mod h1:ypuJS7kP7rUiAn330xG46mj+Nhvym05GM8NqMVekpH0=
github.com/rancher/lasso v0.0.0-20241202185148-04649f379358 h1:pJwgJXPt4fi0ysXsJcl28rvxhx/Z/9SNCDwFOEyeGu0=
github.com/rancher/lasso v0.0.0-20241202185148-04649f379358/go.mod h1:IxgTBO55lziYhTEETyVKiT8/B5Rg92qYiRmcIIYoPgI=
github.com/rancher/lasso v0.0.0-20250123080302-9325fed68518 h1:aElNUJg6kxTvs/e4yfMEkk0Dgzo/lyHl85xDqP+FC+A=
github.com/rancher/lasso v0.0.0-20250123080302-9325fed68518/go.mod h1:stR7zYyew1IOnKYV5vFx1kXX5/pUoKeo5K5c78qAdV8=
github.com/rancher/norman v0.0.0-20241001183610-78a520c160ab h1:ihK6See3y/JilqZlc0CG7NXPN+ue5nY9U7xUZUA8M7I=
github.com/rancher/norman v0.0.0-20241001183610-78a520c160ab/go.mod h1:qX/OG/4wY27xSAcSdRilUBxBumV6Ey2CWpAeaKnBQDs=
github.com/rancher/remotedialer v0.3.2 h1:kstZbRwPS5gPWpGg8VjEHT2poHtArs+Fc317YM8JCzU=
Expand Down
38 changes: 38 additions & 0 deletions pkg/resources/virtual/clusters/clusters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package clusters

import (
"fmt"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// TransformManagedClusters does special-case handling on <management.cattle.io v3 Cluster>s:
// creates a new virtual `status.connected` boolean field that looks for `type = "Ready"` in any
// of the status.conditions records.

func TransformManagedCluster(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
conditions, ok, err := unstructured.NestedFieldNoCopy(obj.Object, "status", "conditions")
if err != nil {
return obj, err
}
if !ok {
return obj, fmt.Errorf("failed to find status.conditions block in cluster %s", obj.GetName())
}
connectedStatus := false
conditionsAsArray, ok := conditions.([]interface{})
if !ok {
return obj, fmt.Errorf("failed to parse status.conditions as array")
}
for _, condition := range conditionsAsArray {
conditionMap, ok := condition.(map[string]interface{})
if !ok {
return obj, fmt.Errorf("failed to parse a condition as a map")
}
if conditionMap["type"] == "Ready" && conditionMap["status"] == "True" {
connectedStatus = true
break
}
}
err = unstructured.SetNestedField(obj.Object, connectedStatus, "status", "connected")
return obj, err
}
Loading

0 comments on commit c180569

Please sign in to comment.