Skip to content

Commit 6ae0ee4

Browse files
committed
APIGOV-31191 improvements and unit tests
1 parent 2ca1d4f commit 6ae0ee4

14 files changed

+1229
-19
lines changed

pkg/agent/agent.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,8 @@ func newHandlers() []handler.Handler {
810810
handler.NewARDHandler(agent.cacheManager),
811811
handler.NewAPDHandler(agent.cacheManager),
812812
handler.NewEnvironmentHandler(agent.cacheManager, agent.cfg.GetCredentialConfig(), envName),
813+
handler.NewDiscoveryManagedApplicationHandler(agent.cacheManager),
814+
handler.NewDiscoveryAccessRequestHandler(agent.cacheManager),
813815
)
814816
case config.TraceabilityAgent:
815817
// Register managed application and access handler for traceability agent

pkg/agent/cache/cachevalidation.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ func (c *cacheManager) getCacheForKind(kind string) cache.Cache {
4848
}
4949
}
5050

51-
// extractResourceSummary iterates the cache keys and returns name -> modifyTimestamp.
51+
// ResourceCacheKey builds a unique cache key from kind, scope name, and resource name.
52+
func ResourceCacheKey(kind, scopeName, name string) string {
53+
return kind + "/" + scopeName + "/" + name
54+
}
55+
56+
// extractResourceSummary iterates the cache keys and returns a composite key -> modifyTimestamp.
5257
func (c *cacheManager) extractResourceSummary(resourceCache cache.Cache) map[string]time.Time {
5358
result := make(map[string]time.Time)
5459
keys := resourceCache.GetKeys()
@@ -63,7 +68,7 @@ func (c *cacheManager) extractResourceSummary(resourceCache cache.Cache) map[str
6368
continue
6469
}
6570
modTime := time.Time(ri.Metadata.Audit.ModifyTimestamp)
66-
result[ri.Name] = modTime
71+
result[ResourceCacheKey(ri.Kind, ri.Metadata.Scope.Name, ri.Name)] = modTime
6772
}
6873

6974
return result
@@ -86,7 +91,7 @@ func (c *cacheManager) getWatchResourcesByKind(group, kind string) map[string]ti
8691
}
8792
if ri.Group == group && ri.Kind == kind {
8893
modTime := time.Time(ri.Metadata.Audit.ModifyTimestamp)
89-
result[ri.Name] = modTime
94+
result[ResourceCacheKey(ri.Kind, ri.Metadata.Scope.Name, ri.Name)] = modTime
9095
}
9196
}
9297

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package cache
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
v1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
8+
management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
9+
defs "github.com/Axway/agent-sdk/pkg/apic/definitions"
10+
"github.com/Axway/agent-sdk/pkg/config"
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func makeRI(group, kind, scopeKind, scopeName, name, id string, modTime time.Time) *v1.ResourceInstance {
15+
return &v1.ResourceInstance{
16+
ResourceMeta: v1.ResourceMeta{
17+
GroupVersionKind: v1.GroupVersionKind{
18+
GroupKind: v1.GroupKind{
19+
Group: group,
20+
Kind: kind,
21+
},
22+
APIVersion: "v1alpha1",
23+
},
24+
Metadata: v1.Metadata{
25+
ID: id,
26+
Scope: v1.MetadataScope{
27+
Kind: scopeKind,
28+
Name: scopeName,
29+
},
30+
Audit: v1.AuditMetadata{
31+
ModifyTimestamp: v1.Time(modTime),
32+
},
33+
},
34+
Name: name,
35+
},
36+
}
37+
}
38+
39+
func makeAPIServiceRI(scopeName, name, apiID string, modTime time.Time) *v1.ResourceInstance {
40+
ri := makeRI("management", management.APIServiceGVK().Kind, "Environment", scopeName, name, apiID, modTime)
41+
ri.SubResources = map[string]interface{}{
42+
defs.XAgentDetails: map[string]interface{}{
43+
defs.AttrExternalAPIID: apiID,
44+
defs.AttrExternalAPIName: name,
45+
},
46+
}
47+
return ri
48+
}
49+
50+
func TestResourceCacheKey(t *testing.T) {
51+
key := ResourceCacheKey("APIService", "env1", "svc1")
52+
assert.Equal(t, "APIService/env1/svc1", key)
53+
54+
key = ResourceCacheKey("APIService", "", "svc1")
55+
assert.Equal(t, "APIService//svc1", key)
56+
}
57+
58+
func TestGetCachedResourcesByKind_APIService(t *testing.T) {
59+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
60+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
61+
62+
ri := makeAPIServiceRI("env1", "svc1", "ext-id-1", modTime)
63+
err := cm.AddAPIService(ri)
64+
assert.Nil(t, err)
65+
66+
result := cm.GetCachedResourcesByKind("management", management.APIServiceGVK().Kind)
67+
expectedKey := ResourceCacheKey(management.APIServiceGVK().Kind, "env1", "svc1")
68+
assert.Len(t, result, 1)
69+
assert.Contains(t, result, expectedKey)
70+
assert.Equal(t, modTime, result[expectedKey])
71+
}
72+
73+
func TestGetCachedResourcesByKind_APIServiceInstance(t *testing.T) {
74+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
75+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
76+
77+
ri := makeRI("management", management.APIServiceInstanceGVK().Kind, "Environment", "env1", "inst1", "id1", modTime)
78+
cm.AddAPIServiceInstance(ri)
79+
80+
result := cm.GetCachedResourcesByKind("management", management.APIServiceInstanceGVK().Kind)
81+
expectedKey := ResourceCacheKey(management.APIServiceInstanceGVK().Kind, "env1", "inst1")
82+
assert.Len(t, result, 1)
83+
assert.Contains(t, result, expectedKey)
84+
}
85+
86+
func TestGetCachedResourcesByKind_ManagedApplication(t *testing.T) {
87+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
88+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
89+
90+
ri := makeRI("management", management.ManagedApplicationGVK().Kind, "Environment", "env1", "app1", "id1", modTime)
91+
cm.AddManagedApplication(ri)
92+
93+
result := cm.GetCachedResourcesByKind("management", management.ManagedApplicationGVK().Kind)
94+
expectedKey := ResourceCacheKey(management.ManagedApplicationGVK().Kind, "env1", "app1")
95+
assert.Len(t, result, 1)
96+
assert.Contains(t, result, expectedKey)
97+
}
98+
99+
func TestGetCachedResourcesByKind_AccessRequest(t *testing.T) {
100+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
101+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
102+
103+
ri := makeRI("management", management.AccessRequestGVK().Kind, "Environment", "env1", "ar1", "id1", modTime)
104+
cm.AddAccessRequest(ri)
105+
106+
result := cm.GetCachedResourcesByKind("management", management.AccessRequestGVK().Kind)
107+
expectedKey := ResourceCacheKey(management.AccessRequestGVK().Kind, "env1", "ar1")
108+
assert.Len(t, result, 1)
109+
assert.Contains(t, result, expectedKey)
110+
}
111+
112+
func TestGetCachedResourcesByKind_MultipleResourcesSameKind(t *testing.T) {
113+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
114+
modTime1 := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
115+
modTime2 := time.Date(2026, 3, 12, 11, 0, 0, 0, time.UTC)
116+
117+
svcKind := management.APIServiceGVK().Kind
118+
ri1 := makeAPIServiceRI("env1", "svc1", "ext-id-1", modTime1)
119+
ri2 := makeAPIServiceRI("env1", "svc2", "ext-id-2", modTime2)
120+
cm.AddAPIService(ri1)
121+
cm.AddAPIService(ri2)
122+
123+
result := cm.GetCachedResourcesByKind("management", svcKind)
124+
assert.Len(t, result, 2)
125+
assert.Contains(t, result, ResourceCacheKey(svcKind, "env1", "svc1"))
126+
assert.Contains(t, result, ResourceCacheKey(svcKind, "env1", "svc2"))
127+
}
128+
129+
func TestGetCachedResourcesByKind_DifferentScopes(t *testing.T) {
130+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
131+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
132+
133+
svcKind := management.APIServiceGVK().Kind
134+
ri1 := makeAPIServiceRI("env1", "svc1", "ext-id-1", modTime)
135+
ri2 := makeAPIServiceRI("env2", "svc1", "ext-id-2", modTime)
136+
cm.AddAPIService(ri1)
137+
cm.AddAPIService(ri2)
138+
139+
result := cm.GetCachedResourcesByKind("management", svcKind)
140+
key1 := ResourceCacheKey(svcKind, "env1", "svc1")
141+
key2 := ResourceCacheKey(svcKind, "env2", "svc1")
142+
143+
assert.Len(t, result, 2)
144+
assert.Contains(t, result, key1)
145+
assert.Contains(t, result, key2)
146+
}
147+
148+
func TestGetCachedResourcesByKind_EmptyCache(t *testing.T) {
149+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
150+
151+
result := cm.GetCachedResourcesByKind("management", management.APIServiceGVK().Kind)
152+
assert.Empty(t, result)
153+
}
154+
155+
func TestGetCachedResourcesByKind_UnknownKindFallsBackToWatchResource(t *testing.T) {
156+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
157+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
158+
159+
ri := makeRI("catalog", "SomeCustomKind", "Environment", "env1", "custom1", "id1", modTime)
160+
cm.AddWatchResource(ri)
161+
162+
result := cm.GetCachedResourcesByKind("catalog", "SomeCustomKind")
163+
expectedKey := ResourceCacheKey("SomeCustomKind", "env1", "custom1")
164+
assert.Len(t, result, 1)
165+
assert.Contains(t, result, expectedKey)
166+
}
167+
168+
func TestGetCachedResourcesByKind_WatchResourceFiltersGroupAndKind(t *testing.T) {
169+
cm := NewAgentCacheManager(&config.CentralConfiguration{}, false)
170+
modTime := time.Date(2026, 3, 12, 10, 0, 0, 0, time.UTC)
171+
172+
ri1 := makeRI("groupA", "KindA", "", "", "res1", "id1", modTime)
173+
ri2 := makeRI("groupB", "KindB", "", "", "res2", "id2", modTime)
174+
cm.AddWatchResource(ri1)
175+
cm.AddWatchResource(ri2)
176+
177+
result := cm.GetCachedResourcesByKind("groupA", "KindA")
178+
assert.Len(t, result, 1)
179+
assert.Contains(t, result, ResourceCacheKey("KindA", "", "res1"))
180+
181+
result = cm.GetCachedResourcesByKind("groupB", "KindB")
182+
assert.Len(t, result, 1)
183+
assert.Contains(t, result, ResourceCacheKey("KindB", "", "res2"))
184+
}

pkg/agent/cachevalidationjob.go

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"fmt"
55
"time"
66

7+
agentcache "github.com/Axway/agent-sdk/pkg/agent/cache"
78
apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
89
management "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/management/v1alpha1"
10+
"github.com/Axway/agent-sdk/pkg/config"
911
"github.com/Axway/agent-sdk/pkg/util/log"
1012
)
1113

@@ -17,26 +19,29 @@ type cacheGetter interface {
1719

1820
// cacheValidator validates the persisted cache against the API server
1921
type cacheValidator struct {
20-
logger log.FieldLogger
21-
client resourceClient
22-
watchTopic *management.WatchTopic
23-
cacheMan cacheGetter
22+
logger log.FieldLogger
23+
client resourceClient
24+
watchTopic *management.WatchTopic
25+
cacheMan cacheGetter
26+
validatedKinds map[string]struct{}
2427
}
2528

2629
func newCacheValidator(
2730
client resourceClient,
2831
watchTopic *management.WatchTopic,
2932
cacheMan cacheGetter,
33+
agentType config.AgentType,
3034
) *cacheValidator {
3135
logger := log.NewFieldLogger().
3236
WithPackage("sdk.agent").
3337
WithComponent("cacheValidator")
3438

3539
return &cacheValidator{
36-
logger: logger,
37-
client: client,
38-
watchTopic: watchTopic,
39-
cacheMan: cacheMan,
40+
logger: logger,
41+
client: client,
42+
watchTopic: watchTopic,
43+
cacheMan: cacheMan,
44+
validatedKinds: validatedKindsByAgentType(agentType),
4045
}
4146
}
4247

@@ -53,9 +58,39 @@ func (cv *cacheValidator) Execute() error {
5358
return nil
5459
}
5560

61+
// validatedKindsByAgentType returns the set of resource kinds to validate for each agent type.
62+
func validatedKindsByAgentType(agentType config.AgentType) map[string]struct{} {
63+
// Common kinds validated by all agent types
64+
kinds := map[string]struct{}{
65+
management.APIServiceGVK().Kind: {},
66+
management.APIServiceInstanceGVK().Kind: {},
67+
}
68+
69+
switch agentType {
70+
case config.DiscoveryAgent:
71+
kinds[management.ManagedApplicationGVK().Kind] = struct{}{}
72+
kinds[management.AccessRequestGVK().Kind] = struct{}{}
73+
kinds[management.AccessRequestDefinitionGVK().Kind] = struct{}{}
74+
kinds[management.CredentialRequestDefinitionGVK().Kind] = struct{}{}
75+
kinds[management.ApplicationProfileDefinitionGVK().Kind] = struct{}{}
76+
case config.TraceabilityAgent:
77+
kinds[management.ManagedApplicationGVK().Kind] = struct{}{}
78+
kinds[management.AccessRequestGVK().Kind] = struct{}{}
79+
case config.ComplianceAgent:
80+
kinds[management.ComplianceRuntimeResultGVK().Kind] = struct{}{}
81+
}
82+
83+
return kinds
84+
}
85+
5686
func (cv *cacheValidator) validateKind(filter management.WatchTopicSpecFilters) bool {
5787
logger := cv.logger.WithField("kind", filter.Kind).WithField("group", filter.Group)
5888

89+
if _, ok := cv.validatedKinds[filter.Kind]; !ok {
90+
logger.Trace("skipping validation for kind")
91+
return true
92+
}
93+
5994
ri := apiv1.ResourceInstance{
6095
ResourceMeta: apiv1.ResourceMeta{
6196
GroupVersionKind: apiv1.GroupVersionKind{
@@ -78,9 +113,9 @@ func (cv *cacheValidator) validateKind(filter management.WatchTopicSpecFilters)
78113
return true
79114
}
80115

81-
// Fetch only name and audit metadata
116+
// Fetch only name, audit metadata, and scope
82117
query := map[string]string{
83-
"fields": "name,metadata.audit",
118+
"fields": "name,kind,metadata.audit,metadata.scope",
84119
}
85120

86121
logger.Trace("fetching resource metadata from API server for validation")
@@ -92,11 +127,11 @@ func (cv *cacheValidator) validateKind(filter management.WatchTopicSpecFilters)
92127

93128
cachedResources := cv.cacheMan.GetCachedResourcesByKind(filter.Group, filter.Kind)
94129

95-
// Build a map from server resources: name -> modifyTimestamp
130+
// Build a map from server resources: composite key -> modifyTimestamp
96131
serverMap := make(map[string]time.Time, len(serverResources))
97132
for _, res := range serverResources {
98133
modTime := time.Time(res.Metadata.Audit.ModifyTimestamp)
99-
serverMap[res.Name] = modTime
134+
serverMap[agentcache.ResourceCacheKey(res.Kind, res.Metadata.Scope.Name, res.Name)] = modTime
100135
}
101136

102137
// Count mismatch

0 commit comments

Comments
 (0)