15
15
package cluster
16
16
17
17
import (
18
+ "context"
19
+ "errors"
18
20
"fmt"
19
21
"time"
20
22
21
- "github.com/redis/go-redis/v9"
23
+ "github.com/argoproj-labs/argocd-agent/internal/cache"
24
+ "github.com/argoproj-labs/argocd-agent/internal/event"
25
+ "github.com/sirupsen/logrus"
22
26
23
- "github.com/argoproj/argo-cd/v3/common"
24
27
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
25
28
cacheutil "github.com/argoproj/argo-cd/v3/util/cache"
26
- appstatecache "github.com/argoproj/argo-cd/v3/util/cache/appstate"
27
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
+ "k8s.io/client-go/kubernetes"
28
31
)
29
32
30
- // UpdateClusterConnectionInfo updates cluster info with connection state and time in mapped cluster
31
- func (m * Manager ) UpdateClusterConnectionInfo (agentName , status string , t time.Time ) {
33
+ // SetAgentConnectionStatus updates cluster info with connection state and time in mapped cluster at principal.
34
+ // This is called when the agent is connected or disconnected with the principal.
35
+ func (m * Manager ) SetAgentConnectionStatus (agentName , status appv1.ConnectionStatus , modifiedAt time.Time ) {
32
36
m .mutex .Lock ()
33
37
defer m .mutex .Unlock ()
34
38
@@ -39,67 +43,135 @@ func (m *Manager) UpdateClusterConnectionInfo(agentName, status string, t time.T
39
43
return
40
44
}
41
45
42
- // Create Redis client and cache
43
- redisOptions := & redis.Options {Addr : m .redisAddress }
44
-
45
- if err := common .SetOptionalRedisPasswordFromKubeConfig (m .ctx , m .kubeclient , m .namespace , redisOptions ); err != nil {
46
- log ().Errorf ("Failed to fetch & set redis password for namespace %s: %v" , m .namespace , err )
47
- }
48
- redisClient := redis .NewClient (redisOptions )
49
-
50
- cache := appstatecache .NewCache (cacheutil .NewCache (
51
- cacheutil .NewRedisCache (redisClient , time .Minute , m .redisCompressionType )), time .Minute )
52
-
53
46
state := "disconnected"
54
47
if status == appv1 .ConnectionStatusSuccessful {
55
48
state = "connected"
56
49
}
57
50
58
- // update the info
59
- if err := cache . SetClusterInfo (cluster .Server ,
51
+ // Update the cluster connection state and time in mapped cluster at principal.
52
+ if err := m . setClusterInfo (cluster .Server , agentName , cluster . Name ,
60
53
& appv1.ClusterInfo {
61
54
ConnectionState : appv1.ConnectionState {
62
55
Status : status ,
63
56
Message : fmt .Sprintf ("Agent: '%s' is %s with principal" , agentName , state ),
64
- ModifiedAt : & metav1.Time {Time : t }}},
65
- ); err != nil {
66
- log ().Errorf ("Failed to update connection info in Cluster: '%s' mapped with Agent: '%s'. Error: %v" , cluster .Name , agentName , err )
57
+ ModifiedAt : & metav1.Time {Time : modifiedAt },
58
+ },
59
+ }); err != nil {
60
+ log ().Errorf ("failed to refresh connection info in cluster: '%s' mapped with agent: '%s'. Error: %v" , cluster .Name , agentName , err )
67
61
return
68
62
}
69
63
70
64
log ().Infof ("Updated connection status to '%s' in Cluster: '%s' mapped with Agent: '%s'" , status , cluster .Name , agentName )
71
65
}
72
66
73
- // refreshClusterConnectionInfo gets latest cluster info from cache and re-saves it to avoid deletion of info
74
- // by ArgoCD after cache expiration time duration (i.e. 10 Minute )
75
- func (m * Manager ) refreshClusterConnectionInfo () {
67
+ // refreshClusterInfo gets latest cluster info from cache and re-saves it to avoid deletion of info
68
+ // by Argo CD after cache expiration time duration (i.e. 10 minutes )
69
+ func (m * Manager ) refreshClusterInfo () {
76
70
m .mutex .Lock ()
77
71
defer m .mutex .Unlock ()
78
72
79
- // iterate through all clusters
73
+ // Iterate through all clusters.
80
74
for agentName , cluster := range m .clusters {
81
- // Create Redis client and cache
82
- redisOptions := & redis.Options {Addr : m .redisAddress }
75
+ clusterInfo , err := GetClusterInfo (m .ctx , m .kubeclient , m .namespace , cluster .Server , m .redisAddress , m .redisCompressionType , cache .SourcePrincipal )
76
+ if err != nil {
77
+ if ! errors .Is (err , cacheutil .ErrCacheMiss ) {
78
+ log ().Errorf ("failed to get connection info from cluster: '%s' mapped with agent: '%s'. Error: %v" , cluster .Name , agentName , err )
79
+ }
80
+ continue
81
+ }
83
82
84
- if err := common .SetOptionalRedisPasswordFromKubeConfig (m .ctx , m .kubeclient , m .namespace , redisOptions ); err != nil {
85
- log ().Errorf ("Failed to fetch & set redis password for namespace %s: %v" , m .namespace , err )
83
+ // Re-save same info.
84
+ if err := m .setClusterInfo (cluster .Server , agentName , cluster .Name , clusterInfo ); err != nil {
85
+ log ().Errorf ("failed to refresh connection info in cluster: '%s' mapped with agent: '%s'. Error: %v" , cluster .Name , agentName , err )
86
+ continue
86
87
}
87
- redisClient := redis .NewClient (redisOptions )
88
+ }
89
+ }
88
90
89
- cache := appstatecache .NewCache (cacheutil .NewCache (
90
- cacheutil .NewRedisCache (redisClient , time .Minute , m .redisCompressionType )), time .Minute )
91
+ // SetClusterCacheStats updates cluster cache info with Application, Resource and API counts in principal.
92
+ // This is called when principal receives clusterCacheInfoUpdate event from agent.
93
+ func (m * Manager ) SetClusterCacheStats (clusterInfo * event.ClusterCacheInfo , agentName string ) error {
94
+ m .mutex .Lock ()
95
+ defer m .mutex .Unlock ()
91
96
92
- // fetch latest info
93
- clusterInfo := & appv1.ClusterInfo {}
94
- if err := cache .GetClusterInfo (cluster .Server , clusterInfo ); err != nil {
95
- log ().Errorf ("Failed to get connection info from Cluster: '%s' mapped with Agent: '%s'. Error: %v" , cluster .Name , agentName , err )
96
- return
97
- }
97
+ // Check if we have a mapping for the requested agent.
98
+ cluster := m .mapping (agentName )
99
+ if cluster == nil {
100
+ log ().Errorf ("agent %s is not mapped to any cluster" , agentName )
101
+ return fmt .Errorf ("agent %s is not mapped to any cluster" , agentName )
102
+ }
103
+
104
+ // Get existing cluster info to preserve cluster connection status
105
+ existingClusterInfo , err := GetClusterInfo (m .ctx , m .kubeclient , m .namespace , cluster .Server , m .redisAddress , m .redisCompressionType , cache .SourcePrincipal )
106
+ if err != nil && ! errors .Is (err , cacheutil .ErrCacheMiss ) {
107
+ log ().Errorf ("failed to get existing cluster info for cluster: '%s' mapped with agent: '%s'. Error: %v" , cluster .Name , agentName , err )
108
+ // Continue with default values if we can't get existing info
109
+ }
110
+
111
+ // Create new cluster info with cache stats
112
+ newClusterInfo := & appv1.ClusterInfo {
113
+ ApplicationsCount : clusterInfo .ApplicationsCount ,
114
+ CacheInfo : appv1.ClusterCacheInfo {
115
+ APIsCount : clusterInfo .APIsCount ,
116
+ ResourcesCount : clusterInfo .ResourcesCount ,
117
+ },
118
+ }
98
119
99
- // re-save same info
100
- if err := cache .SetClusterInfo (cluster .Server , clusterInfo ); err != nil {
101
- log ().Errorf ("Failed to refresh connection info in Cluster: '%s' mapped with Agent: '%s'. Error: %v" , cluster .Name , agentName , err )
102
- return
120
+ // Preserve existing cluster connection status
121
+ if existingClusterInfo != nil {
122
+ newClusterInfo .ConnectionState = existingClusterInfo .ConnectionState
123
+ if existingClusterInfo .CacheInfo .LastCacheSyncTime != nil {
124
+ newClusterInfo .CacheInfo .LastCacheSyncTime = existingClusterInfo .CacheInfo .LastCacheSyncTime
103
125
}
104
126
}
127
+
128
+ // Set the info in mapped cluster at principal.
129
+ if err := m .setClusterInfo (cluster .Server , agentName , cluster .Name , newClusterInfo ); err != nil {
130
+ log ().Errorf ("failed to update cluster cache stats in cluster: '%s' mapped with agent: '%s'. Error: %v" , cluster .Name , agentName , err )
131
+ return err
132
+ }
133
+
134
+ log ().WithFields (logrus.Fields {
135
+ "applicationsCount" : clusterInfo .ApplicationsCount ,
136
+ "apisCount" : clusterInfo .APIsCount ,
137
+ "resourcesCount" : clusterInfo .ResourcesCount ,
138
+ "cluster" : cluster .Name ,
139
+ "agent" : agentName ,
140
+ }).Infof ("Updated cluster cache stats in cluster." )
141
+
142
+ return nil
143
+ }
144
+
145
+ // setClusterInfo saves the given ClusterInfo in the cache.
146
+ func (m * Manager ) setClusterInfo (clusterServer , agentName , clusterName string , clusterInfo * appv1.ClusterInfo ) error {
147
+
148
+ // Get cluster cache instance from redis.
149
+ clusterCache , err := cache .GetCacheInstance (m .ctx , m .kubeclient , m .namespace , m .redisAddress , m .redisCompressionType , cache .SourcePrincipal )
150
+ if err != nil {
151
+ return fmt .Errorf ("failed to get cluster cache instance: %v" , err )
152
+ }
153
+
154
+ // Save the given cluster info in cache.
155
+ if err := clusterCache .SetClusterInfo (clusterServer , clusterInfo ); err != nil {
156
+ return fmt .Errorf ("failed to refresh connection info in cluster: '%s' mapped with agent: '%s': %v" , clusterName , agentName , err )
157
+ }
158
+ return nil
159
+ }
160
+
161
+ // GetClusterInfo retrieves the ClusterInfo for a given cluster from the cache.
162
+ func GetClusterInfo (ctx context.Context , kubeclient kubernetes.Interface , namespace ,
163
+ clusterServer , redisAddress string , redisCompressionType cacheutil.RedisCompressionType , role string ) (* appv1.ClusterInfo , error ) {
164
+
165
+ // Get cluster cache instance from redis.
166
+ clusterCache , err := cache .GetCacheInstance (ctx , kubeclient , namespace , redisAddress , redisCompressionType , role )
167
+ if err != nil {
168
+ return nil , fmt .Errorf ("failed to get cluster cache instance: %v" , err )
169
+ }
170
+
171
+ clusterInfo := & appv1.ClusterInfo {}
172
+ // Fetch the cluster info from cache.
173
+ if err := clusterCache .GetClusterInfo (clusterServer , clusterInfo ); err != nil {
174
+ return nil , err
175
+ }
176
+ return clusterInfo , nil
105
177
}
0 commit comments