1
0
mirror of https://github.com/rancher/steve.git synced 2025-09-17 15:58:41 +00:00

Add an attribute observedgeneration (#714)

* Add an attribute observedgeneration

* add unit tests
This commit is contained in:
Sakala Venkata Krishna Rohit
2025-08-07 10:30:06 -07:00
committed by GitHub
parent a020084518
commit 6946ffe8aa
9 changed files with 137 additions and 21 deletions

2
go.mod
View File

@@ -26,7 +26,7 @@ require (
github.com/rancher/lasso v0.2.3
github.com/rancher/norman v0.7.0
github.com/rancher/remotedialer v0.4.5-rc.3
github.com/rancher/wrangler/v3 v3.2.2
github.com/rancher/wrangler/v3 v3.2.3-rc.2
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/urfave/cli/v2 v2.27.7

4
go.sum
View File

@@ -226,8 +226,8 @@ github.com/rancher/norman v0.7.0 h1:duBZxekBj13k/2RTyWKZgV/ntXkIXm0sRKqwFO8ui+I=
github.com/rancher/norman v0.7.0/go.mod h1:IOQn3CNCms6UK72QHujesLKedqZh4+SP8/FDEFc+7Ns=
github.com/rancher/remotedialer v0.4.5-rc.3 h1:Bfik0ZF89Bpm13ft1GPVg3/0xzCO+c0N0yD9jmlavXc=
github.com/rancher/remotedialer v0.4.5-rc.3/go.mod h1:N96a/IQXoP9JLc9cbeJEly3fLi8lnpXFeFOJofgJBbA=
github.com/rancher/wrangler/v3 v3.2.2 h1:IK1/v8n8gaZSB4izmJhGFXJt38Z8gkbwzl3Lo/e2jQc=
github.com/rancher/wrangler/v3 v3.2.2/go.mod h1:TA1QuuQxrtn/kmJbBLW/l24IcfHBmSXBa9an3IRlqQQ=
github.com/rancher/wrangler/v3 v3.2.3-rc.2 h1:CnbO8lT8ZwQF4PfrptfCwmYLfQBQR9BRxGvOtEiZZKU=
github.com/rancher/wrangler/v3 v3.2.3-rc.2/go.mod h1:TA1QuuQxrtn/kmJbBLW/l24IcfHBmSXBa9an3IRlqQQ=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=

View File

@@ -52,7 +52,7 @@ type clusterCache struct {
sync.RWMutex
ctx context.Context
summaryClient client.Interface
summaryClient client.ExtendedInterface
watchers map[schema2.GroupVersionKind]*watcher
workqueue workqueue.DelayingInterface
@@ -64,7 +64,7 @@ type clusterCache struct {
func NewClusterCache(ctx context.Context, dynamicClient dynamic.Interface) ClusterCache {
c := &clusterCache{
ctx: ctx,
summaryClient: client.NewForDynamicClient(dynamicClient),
summaryClient: client.NewForExtendedDynamicClient(dynamicClient),
watchers: map[schema2.GroupVersionKind]*watcher{},
workqueue: workqueue.NewNamedDelayingQueue("cluster-cache"),
}
@@ -147,7 +147,10 @@ func (h *clusterCache) OnSchemas(schemas *schema.Collection) error {
continue
}
summaryInformer := informer.NewFilteredSummaryInformer(h.summaryClient, gvr, metav1.NamespaceAll, 2*time.Hour,
opts := &client.Options{
Schema: schema.Schema,
}
summaryInformer := informer.NewFilteredSummaryInformerWithOptions(h.summaryClient, gvr, opts, metav1.NamespaceAll, 2*time.Hour,
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, nil)
ctx, cancel := context.WithCancel(h.ctx)
w := &watcher{

View File

@@ -29,7 +29,7 @@ func Register(ctx context.Context, apiSchemas *types.APISchemas, cg proxy.Client
schema.ResourceMethods = []string{http.MethodGet}
schema.Attributes["access"] = accesscontrol.AccessListByVerb{
"watch": accesscontrol.AccessList{
{
accesscontrol.Access{
Namespace: "*",
ResourceName: "*",
},

View File

@@ -10,6 +10,7 @@ import (
"github.com/rancher/steve/pkg/accesscontrol"
"github.com/rancher/steve/pkg/attributes"
"github.com/rancher/steve/pkg/clustercache"
"github.com/rancher/wrangler/v3/pkg/schemas"
"github.com/rancher/wrangler/v3/pkg/summary"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
@@ -31,7 +32,7 @@ func Register(schemas *types.APISchemas, ccache clustercache.ClusterCache) {
schema.ResourceMethods = []string{http.MethodGet}
schema.Attributes["access"] = accesscontrol.AccessListByVerb{
"watch": accesscontrol.AccessList{
{
accesscontrol.Access{
Namespace: "*",
ResourceName: "*",
},
@@ -151,7 +152,7 @@ func (s *Store) Watch(apiOp *types.APIRequest, schema *types.APISchema, w types.
return nil
}
_, namespace, revision, summary, ok := getInfo(obj)
_, namespace, revision, summary, ok := getInfo(obj, schema)
if !ok {
return nil
}
@@ -162,7 +163,7 @@ func (s *Store) Watch(apiOp *types.APIRequest, schema *types.APISchema, w types.
}
if oldObj != nil {
if _, _, _, oldSummary, ok := getInfo(oldObj); ok {
if _, _, _, oldSummary, ok := getInfo(oldObj, schema); ok {
if oldSummary.Transitioning == summary.Transitioning &&
oldSummary.Error == summary.Error &&
simpleState(oldSummary) == simpleState(summary) {
@@ -230,7 +231,7 @@ func (s *Store) schemasToWatch(apiOp *types.APIRequest) (result []*types.APISche
return
}
func getInfo(obj interface{}) (name string, namespace string, revision int, summaryResult summary.Summary, ok bool) {
func getInfo(obj interface{}, schema *types.APISchema) (name string, namespace string, revision int, summaryResult summary.Summary, ok bool) {
r, ok := obj.(runtime.Object)
if !ok {
return "", "", 0, summaryResult, false
@@ -246,7 +247,12 @@ func getInfo(obj interface{}) (name string, namespace string, revision int, summ
return "", "", 0, summaryResult, false
}
summaryResult = summary.Summarize(r)
opts := &summary.SummarizeOptions{HasObservedGeneration: false}
if schema != nil && schema.Attributes != nil {
opts.HasObservedGeneration = schemas.HasObservedGeneration(schema.Schema)
}
summaryResult = summary.SummarizeWithOptions(r, opts)
return meta.GetName(), meta.GetNamespace(), revision, summaryResult, true
}
@@ -324,7 +330,7 @@ func (s *Store) getCount(apiOp *types.APIRequest) Count {
all := access.Grants("list", "*", "*")
for _, obj := range s.ccache.List(gvk) {
name, ns, revision, summary, ok := getInfo(obj)
name, ns, revision, summary, ok := getInfo(obj, schema)
if !ok {
continue
}

View File

@@ -77,5 +77,27 @@ func forVersion(group, kind string, version v1.CustomResourceDefinitionVersion,
}
if version.Schema != nil && version.Schema.OpenAPIV3Schema != nil {
schema.Description = version.Schema.OpenAPIV3Schema.Description
if hasObservedGeneration(version.Schema.OpenAPIV3Schema) {
schemas.SetHasObservedGeneration(schema.Schema, true)
}
}
}
func hasObservedGeneration(schema *v1.JSONSchemaProps) bool {
if schema == nil {
return false
}
if schema.Properties == nil {
return false
}
status, ok := schema.Properties["status"]
if !ok {
return false
}
if status.Properties == nil {
return false
}
_, found := status.Properties["observedGeneration"]
return found
}

View File

@@ -306,3 +306,73 @@ func TestAddCustomResources(t *testing.T) {
})
}
}
func TestHasObservedGeneration(t *testing.T) {
tests := []struct {
name string
schema *v1.JSONSchemaProps
expected bool
}{
{
name: "nil schema",
schema: nil,
expected: false,
},
{
name: "nil properties",
schema: &v1.JSONSchemaProps{},
expected: false,
},
{
name: "no status property",
schema: &v1.JSONSchemaProps{
Properties: map[string]v1.JSONSchemaProps{
"foo": {},
},
},
expected: false,
},
{
name: "status property without properties",
schema: &v1.JSONSchemaProps{
Properties: map[string]v1.JSONSchemaProps{
"status": {},
},
},
expected: false,
},
{
name: "status property with properties but no observedGeneration",
schema: &v1.JSONSchemaProps{
Properties: map[string]v1.JSONSchemaProps{
"status": {
Properties: map[string]v1.JSONSchemaProps{
"foo": {},
},
},
},
},
expected: false,
},
{
name: "status property with observedGeneration",
schema: &v1.JSONSchemaProps{
Properties: map[string]v1.JSONSchemaProps{
"status": {
Properties: map[string]v1.JSONSchemaProps{
"observedGeneration": {},
},
},
},
},
expected: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
result := hasObservedGeneration(tt.schema)
assert.Equal(t, tt.expected, result)
})
}
}

View File

@@ -4,9 +4,9 @@ import (
"context"
"time"
"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io"
apiextensions "github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io"
apiextensionsv1 "github.com/rancher/wrangler/v3/pkg/generated/controllers/apiextensions.k8s.io/v1"
"github.com/rancher/wrangler/v3/pkg/generated/controllers/apiregistration.k8s.io"
apiregistration "github.com/rancher/wrangler/v3/pkg/generated/controllers/apiregistration.k8s.io"
apiregistrationv1 "github.com/rancher/wrangler/v3/pkg/generated/controllers/apiregistration.k8s.io/v1"
"github.com/rancher/wrangler/v3/pkg/generated/controllers/core"
corev1 "github.com/rancher/wrangler/v3/pkg/generated/controllers/core/v1"

View File

@@ -10,6 +10,7 @@ import (
"github.com/rancher/steve/pkg/clustercache"
"github.com/rancher/steve/pkg/schema"
"github.com/rancher/steve/pkg/schema/converter"
wranglerSchemas "github.com/rancher/wrangler/v3/pkg/schemas"
"github.com/rancher/wrangler/v3/pkg/slice"
"github.com/rancher/wrangler/v3/pkg/summary"
"k8s.io/apimachinery/pkg/api/meta"
@@ -106,7 +107,7 @@ func (s *SummaryCache) SummaryAndRelationship(obj runtime.Object) (*summary.Summ
defer s.RUnlock()
key := toKey(obj)
summarized := summary.Summarized(obj)
summarized := summary.SummarizedWithOptions(obj, getSummarizeOptions(obj, s.schemas))
relObjs, err := s.cache.ByIndex(relationshipIndex, key)
if err != nil {
@@ -183,7 +184,7 @@ func (s *SummaryCache) toRel(ns string, rel *summary.Relationship) Relationship
FromID: id,
FromType: converter.GVKToSchemaID(runtimeschema.FromAPIVersionAndKind(rel.APIVersion, rel.Kind)),
Rel: rel.Type,
}, obj)
}, obj, s.schemas)
}
toNS := ""
@@ -197,10 +198,10 @@ func (s *SummaryCache) toRel(ns string, rel *summary.Relationship) Relationship
Rel: rel.Type,
ToNamespace: toNS,
Selector: toSelector(rel.Selector),
}, obj)
}, obj, s.schemas)
}
func addObject(rel Relationship, obj interface{}) Relationship {
func addObject(rel Relationship, obj interface{}, schemas *schema.Collection) Relationship {
if obj == nil {
return rel
}
@@ -210,7 +211,8 @@ func addObject(rel Relationship, obj interface{}) Relationship {
return rel
}
summarized := summary.Summarized(ro)
summarized := summary.SummarizedWithOptions(ro, getSummarizeOptions(ro, schemas))
rel.State = summarized.State
rel.Error = summarized.Error
rel.Message = strings.Join(summarized.Message, "; ")
@@ -267,7 +269,7 @@ func (s *SummaryCache) Change(newObj, oldObj runtime.Object) {
func (s *SummaryCache) process(obj runtime.Object) (*summary.SummarizedObject, []*summary.Relationship) {
var (
rels []*summary.Relationship
summary = summary.Summarized(obj)
summary = summary.SummarizedWithOptions(obj, getSummarizeOptions(obj, s.schemas))
)
for _, rel := range summary.Relationships {
@@ -339,6 +341,19 @@ func (s *SummaryCache) OnChange(_ runtimeschema.GroupVersionKind, key string, ob
return nil
}
func getSummarizeOptions(obj runtime.Object, schemas *schema.Collection) *summary.SummarizeOptions {
gvk := obj.GetObjectKind().GroupVersionKind()
schemaID := converter.GVKToSchemaID(gvk)
schema := schemas.Schema(schemaID)
opts := &summary.SummarizeOptions{HasObservedGeneration: false}
if schema != nil && schema.Attributes != nil {
opts.HasObservedGeneration = wranglerSchemas.HasObservedGeneration(schema.Schema)
}
return opts
}
func toKeyFrom(namespace, name string, gvk runtimeschema.GroupVersionKind, other ...string) string {
parts := []string{
gvk.Group,