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:
committed by
GitHub
parent
a020084518
commit
6946ffe8aa
2
go.mod
2
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
@@ -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{
|
||||
|
@@ -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: "*",
|
||||
},
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -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"
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user